前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java学习之类与继承

Java学习之类与继承

作者头像
cherishspring
发布2019-10-14 16:37:28
5160
发布2019-10-14 16:37:28
举报
文章被收录于专栏:Java学习笔记Java学习笔记

类与继承

1,什么是类?

在java中,类文件是以.java为后缀的代码文件,在每个类文件中最多只允许出现一个public类,当有public类时,类文件的名称必须和public类的名称相同,若不存在public,则类文件名称可以为任意的名称(数字除外)。

  • 对于char short byte int long float double等基本数据类型的变量来说会默认初始化为0(boolean变量会默认初始化为false)。
  • 对于应用类型的变量会默认初始化为null。如果没有显示地定义构造器,则编译器会自动创建一个无参构造器,若显示地定义了构造器。编译器就不会自动添加构造器。注:所有构造器默认为static的。
初始化顺序

当程序执行的时候,类的static成员变量会被初始化。另外,若类中有static语句块,则会执行static语句块。Static成员变量和static语句块的执行顺序同代码中的顺序一致。Java中类是按需加载,只有当需要用到这个类的时候,才会加载这个类,并且只会加载一次。

代码语言:javascript
复制
public class Test {
	public static void main(String[] args) throws ClassNotFoundException{
		Bread bread1=new Bread();
		Bread bread2=new Bread();
	}
}
class Bread{
	static{
		System.out.println("Bread is loaded");
	}
	public Bread(){
		System.out.println("bread");
	}
}

运行结果: Bread is loaded bread bread 会发现Bread is loaded只会被打印一次。在生成对象的过程中,会先初始化对象的成员变量,然后在执行构造器。

2,什么是继承

继承是所有OOP语言不可缺少的部分,在java中使用extends关键字来表示继承关系。当创建类时,总是在继承。若没有明确指出要继承的类,就总是隐式地从根类Object进行继承。

代码语言:javascript
复制
class Person {
public Person(){
	
}
}
class Man extends Person{
	public Man(){
		
	}
}

类Man继承于Person类,Person类称为父类(基类),Man类称为子类。若两个类存在继承关系,那么子类会自动继承父类的方法和变量。在Java中,类只能单继承,即一个类最多只能显示地继承于一个父类。但一个类可被多个类继承,即一个类可以有多个子类。

<1>,子类继承父类的成员方法
  • 能够继承父类的public和protected成员变量;不能继承父类的private成员变量。
  • 对于父类的包访问权限成员变量,若子类和父类在同一包下,则子类能够继承。否则,子类不能继承。
  • 对于子类能继承的父类成员变量。如在子类中出现了同名称的成员变量,则会发生隐藏现象。即子类的成员变量会屏蔽掉父类的同名成员变量。若要在子类中访问父类中的同名成员变量,需要使用super关键字进行引用。
<2>,子类继承父类的方法
  • 能够继承父类的public和protected成员方法;不能继承父类的private成员方法。 对于父类的包访问权限成员方法。若子类和父类在同一包下,则子类能够继承。否则,子类不能继承。
  • 对于子类可以继承的父类成员方法。若在子类中出现了同名称的成员方法,称为覆盖。即子类的成员方法会覆盖掉父类的同名成员方法。若要在子类中访问父类父类中同名成员方法,需要使用super关键字引用。 注:隐藏与覆盖不同。隐藏是针对成员变量和静态方法的,覆盖是针对普通方法的。
  • 覆盖只针对非静态方法(终态方法不能被继承,所以就存在覆盖一说),而隐藏是针对成员变量和静态方法的。这两者的区别是:覆盖受RTTI约束,而隐藏却不受该约束。即只有覆盖方法才会进行动态绑定,而隐藏是不会发生动态绑定的。在Java中,除了static方法和final方法,其他所有方法都是动态绑定。
<3>,构造器

子类不能继承父类的构造器。如果父类的构造器都是带有参数的,则必须在子类的构造器中显示地通过super关键字调用父类的构造器并配以适当的参数列表。如果父类有无参构造器,则在子类的构造器中用super关键字调用父类构造器不是必须的。如果没有使用super关键字,系统会自动调用父类的无参构造器。

代码语言:javascript
复制
class Shape {//父类
	protected String name;
	
	public Shape(){//无参构造器
		name="shape";
	}
	public Shape(String name){//有参构造器
		this.name=name;
	}
}
//子类Circle继承父类Shape
class Circle extends Shape{
	private double radius;
	
	public Circle(){
		radius=0;
	}
	public Circle(double radius){
		this.radius=radius;
	}
	public Circle(double radius,String name){
		this.radius=radius;
		this.name=name;
	}
}

可以改成:

代码语言:javascript
复制
class Shape {//父类
	protected String name;
	/*将无参构造器去掉,则必须在子类的构造器中显示地通过super关键字调用父类的构造器并配以适当的参数列表。
*/
	/*public Shape(){//无参构造器
		name="shape";
	}*/
	public Shape(String name){//有参构造器
		this.name=name;
	}
}
//子类Circle继承父类Shape
class Circle extends Shape{
	private double radius;
	
	public Circle(){
		super("cicle");
		radius=0;
	}
	public Circle(double radius){
		super("cicle");
		this.radius=radius;
	}
	public Circle(double radius,String name){
		super(name);
		this.radius=radius;
		this.name=name;
	}
}
<3>,子类继承父类的方法

super主要有两种用法:

  • super.成员变量/super.成员方法;
  • super(parameter1,parameter2…) 第一种用法主要用来在子类中调用父类的同名成员变量或者方法;第二种主要用在子类的构造器中显示地调用父类构造器。要注意的是,如果用在子类构造器中,则必须是子类构造器的第一个语句。

super在Java中的运用

  • 重写父类方法的情况:
代码语言:javascript
复制
public class A {
    String name = "lly";
    protected void getName(){
        System.out.println("父类getName->"+ name);
    }
}
public class B extends A {
    String nameB = "llyB";
    @Override
    protected void getName() {
        System.out.println("子类getName->"+nameB);
        super.getName();
    }
    public static void main(String[] args) {
        B b = new B();
        b.getName();
    }
}

打印如下: 子类getName->llyB 父类getName->lly

1.在子类B中,我们重写了父类的getName方法,如果在重写的getName方法中我们去调用了父类的相同方法,必须要通过super关键字显示的指明出来。 2.如果不明确出来,按照子类优先的原则,相当于还是再调用重写的getName()方法,此时就形成了死循环,执行后会报java.lang.StackOverflowError异常

  • 重写父类变量的情况
代码语言:javascript
复制
public class B extends A {
    String name = "llyB";
    @Override
    protected void getName() {
        name = super.name;
        System.out.println("子类getName->"+name);
    }
    public static void main(String[] args) {
        B b = new B();
        b.getName();
    }
}

此时子类B中有一个和父类一样的字段(也可以说成父类字段被隐藏了),为了获得父类的这个字段我们就必须加上super,如果没有加,直接写成name= name;不会报错,只是会警告,表示此条语句没有任何意义,因为此时都是访问的子类B里面的那么字段。我们通过super是不能访问父类private修饰的变量和方法的,因为这个只属于父类的内部成员,一个对象是不能访问它的private成员的。

  • 在子类的构造方法中 编译器会自动在子类构造函数的第一句加上 super(); 来调用父类的无参构造器;此时可以省略不写。如果想写上的话必须在子类构造函数的第一句,可以通过super来调用父类其他重载的构造方法,只要相应的把参数传过去就好。

因此,super的作用主要在下面三种情况下: 1、调用父类被子类重写的方法; 2、调用父类被子类重定义的字段(被隐藏的成员变量); 3、调用父类的构造方法; 其他情况,由于子类自动继承了父类相应属性方法,关键字super可以不显示写出来。

关于构造方法

如果一个类中没有写任何的构造方法,JVM会生成一个默认的无参构造方法。在继承关系中,由于在子类的构造方法中,第一条语句默认为调用父类的无参构造方法(即默认为super(),一般这句话省略了)。所以当在父类中定义了有参构造函数,都是没有定义无参构造函数时,IDE会强制要求我们定义一个相同参数类型的构造器。这也是我们在Android中自定义组件去继承其他View是经常被要求定义几个构造函数的原因。

以下子类B的情形是错误不能通过编译的:

代码语言:javascript
复制
public class A {
    public A(String s){  }
}
public class B extends A {    //编译错误,JVM默认给B加了一个无参构造方法,而在这个方法中默认调用了super(),但是父类中并不存在该构造方法
    String name = "llyB";
}
public class B extends A {    //同样编译错误,相同的道理,虽然我们在子类中自己定义了一个构造方法,但是在这个构造方法中还是默认调用了super(),但是父类中并不存在该构造方法
    String name = "llyB";
    public B(String s){}
}

此时就需要显示的去调用父类构造方法了,如下:

代码语言:javascript
复制
public class B extends A {    //正确编译
    String name = "llyB";
    public B(String s){
        super(s);
    }
}

所以,只要记住,在子类的构造方法中,只要里面没有显示的通过super去调用父类相应的构造方法,默认都是调用super(),即无参构造方法,因此要确保父类有相应的构造方法。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019 年 09 月,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1,什么是类?
    • 初始化顺序
    • 2,什么是继承
      • <1>,子类继承父类的成员方法
        • <2>,子类继承父类的方法
          • <3>,构造器
            • <3>,子类继承父类的方法
            • super在Java中的运用
              • 关于构造方法
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档