继承
提取出一些共性特征,作为父类,子类就可以继承父类的这些开放成员,子类再添加自己独有的属性和方法。如果再有类具有这些共同特征,也可继承这个父类。
特点:1.利于代码复用 2.缩短开发周期
继承是一种类与类之间的关系。
使用已存在的类的定义作为基础建立新类。
子类(派生类)——|>父类(基类)
新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类的特征(即继承全部开放特征)。
满足“A is a B”的逻辑关系。如:猫和狗都继承动物类,学生和老师都继承人类。
继承的实现(extends)
父类
class Animal{
//公共的属性和方法
}
子类,只能继承一个父类
class Dog extends Animal{
//子类特有的属性和方法
}
class Cat extends Animal{
}
子类可以访问父类非私有成员【把父类的非私有(private修饰但提供了getter/setter方法不是私有)成员(属性和方法)当自己的用】。
父类不可以访问子类特有成员(即时public也不行)。
方法重写&方法重载
先回顾一下方法重载
1.同一个类中
2.方法名相同,参数列表不同(参数顺序、个数、类型),与参数名无关。
3.与方法返回值类型,访问修饰符无关。
方法重写:子类也具有父类的方法,但实现的功能有所不同,需要重写。即与父类同名的方法。
1.有继承关系的子类中。
2.子类重写父类的方法。
3.返回值类型,方法名,参数列表(参数类型、顺序、个数)都有与父类继承的方法相同,与方法的参数名无关(参数名相同与否均可)。
注:返回值类型,可以是子类类型。父类方法的返回值是父类类型,子类重写方法时,其返回值类型既可以是父类类型,也可以是子类类型,但只能向下兼容,不能向上兼容,不能是Object类。
如:父类 public Animal create(){ return new Animal();}
子类 @Override
public Dog create(){ return new Dog();}
4.子类方法的访问修饰符的访问范围需要大于等于父类的访问范围。private<default<protected<public
当子类重写父类方法后,子类对象调用的是重写后的方法,子类定义与父类同名的方法,要重写;子类可以定义与父类重名的属性。
super:父类对象的引用
子类可以把父类的开放成员(属性和方法)当作自己的用,子类包括继承父类的方法和自己重写父类的的方法。在子类的方法中,调用其它其它成员方法,在包含父类方法以及重写父类的方法情况下,子类肯定是调用重写的方法,那么如果想要调用父类的方法,而不是重写的那个方法,可以用super关键字来调用。如:super.eat(); super.setAge(3); 父类的属性可以用super调用,或者子类直接用就行。假如父类有name属性,子类不再定义了,如果成员方法或构造方法中用name属性,可以直接用或者super.name。
当然了,如果父类的方法没有被重写,子类对象和子类方法中在调用父类方法时如同自己的一样,子类在调用父类的开放属性时也是同自己定义的一样去使用。
父类的构造方法不允许被继承,也就不允许被重写了。
在有继承关系的程序初始化顺序:
在Java中,当实例化对象时,对象所在类的所有成员变量首先要进行初始化,只有当所有类成员完成初始化后,才会调用对象所在类的构造函数创建对象。
父类静态成员——>子类静态成员——>父类对象构造——>子类对象构造
静态成员【静态变量(也就是static成员属性)和静态代码块】,其中访问修饰符不影响成员加载顺序,谁先谁后与书写位置有关。
假设静态变量在静态代码块前面:(如果出现位置相反,就反过来)
父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量(成员属性)、父类构造代码块、父类构造函数、子类非静态变量、子类构造代码块、子类构造函数。
(1)静态优先非静态,静态属于类加载,只初始化一次,非静态属于对象,随着对象的创建可能会初始化多次。
(2)父类优先于子类进行初始化。Object类是超类(顶级父类。
(3)成员变量按出现顺序进行初始化,即使在方法中,也在任何方法被调用前(包括构造方法)先初始化。
构造方法的调用必须放在构造方法里,子类构造(包括带参构造)默认调用父类无参构造方法,如果要调用父类带参构造,可以通过super(参)调用父类允许被访问的其它构造方法。super必须在子类构造方法中的第一行。如:super(name,month);
super小结:
代表父类引用。
-访问父类成员方法 super.print();
-访问父类属性 super.name;
-访问父类构造方法 super();
(1)子类的构造的过程中必须调用其父类的构造方法(一层一层往上找,直到Object类)。
(2)如果子类的构造方法中没写super,则系统默认调用父类的无参构造方法,没显式写无参构造的,系统默认提供。
(3)如果父类中写了带参构造却没写无参构造,在子类的构造方法中,又没写super(参),则编译出错。【因为父类写带参构造了,系统就不会再提供无参构造了,而子类不写super(参),就会默认去调用父类的无参构造去,找不到,就出错。】如果父类既写了无参构造又写了带参构造,子类的构造方法中不写super,默认调用父类无参构造,写了super();也是调用无参构造,写了super(参),调用带参构造。其中子类的构造方法包括带参构造和无参构造都符合上述说法,创建对象时调用哪个构造,哪个构造都要遵守上述规则。super();就是调用父类无参构造,可写可不写,super(参)是调用指定的父类的带参构造。
(4)使用super(); super(参); 必须放在子类构造方法中的第一行。
super&this
在构造方法(有参构造)中写this(); 是先调用同类中的无参构造,this();也必须写在第一行。所以构造方法间调用时,this与super不能共存。
this:当前类对象的引用
-访问当前类的成员方法
-访问当前类的成员属性
-访问当前类的构造方法
-不能在静态方法中使用
super:父类对象的引用
-访问父类的成员方法
-访问父类的成员属性
-访问父类的构造方法
-不能在静态方法中使用
Object类
-Object类是所有类的父类。
-一个类没有使用extends关键字明确表示继承关系,则默认继承Object类(包括数组)。
-Java中的每个类都可以使用Object中定义的方法。【Object类是java.lang包里的(String、System...),此包系统默认加载,不要手动import】
-Object类中的equals方法和"=="是一样的,看地址是否相同,即是否指向同一个对象。String类中重写了Object类中的equals方法,比较对象的内容是否相同。
public class TestOne { public static void main(String[] args){ Animal one=new Animal("小one",2); Animal two=new Animal("小one",2); //equals测试:继承Object类中的equals方法时,比较的是两个引用是否指向同一个对象。 boolean flag=one.equals(two); System.out.println("one和two的引用比较:"+flag); System.out.println("one和two的引用比较:"+(one==two)); System.out.println("=============================="); //String类重写了Object类的equals方法,比较的是对象的内容是否相同 String str1=new String("hello"); String str2=new String("hello"); flag=str1.equals(str2); System.out.println("str1和str2的引用比较:"+flag); System.out.println("str1和str2的引用比较:"+(str1==str2)); } } //运行结果 小one2岁 小one2岁 one和two的引用比较:false one和two的引用比较:false ============================== str1和str2的引用比较:true str1和str2的引用比较:false
那如何比较两个对象的内容值?
需要重写Object类中的equals方法
在Animal类中重写equals方法,使得比较对象的内容
public boolean equals(Object obj){ if(obj==null) //如果传入的对象为空,防止出现NullPointerException return false; Animal temp=(Animal)obj; //把传入的对象obj强转成Animal类型 if(this.getName().equals(temp.getName())&&this.getAge()==temp.getAge()){ return true; } else return false; } //运行结果 one和two的引用比较:true one和two的引用比较:false ============================== str1和str2的引用比较:true str1和str2的引用比较:false
在Animal类中重载equals方法,使得比较对象的内容,不用类型强制转换,直接传入Animal类型
public boolean equals(Animal animal){ if(animal==null) return false; if(this.getName().equals(animal.getName())&&this.getAge()==animal.getAge()){ return true; } else return false; } //运行结果 one和two的引用比较:true one和two的引用比较:false ============================== str1和str2的引用比较:true str1和str2的引用比较:false
如果重写和重载这两段代码都放在Animal类中,主函数中对象调用的是后者,因为后者通过Animal类型参数重载了equals方法,对象调用时会自动定位类型匹配方法。
Object类中的toString方法
返回这个类的字符串表现形式:类名+@+地址哈希值
(1)输出对象名时,默认会直接调用类中的toString
(2)继承Object中的toString方法时,输出对象的字符串表现形式:类型信息+@+地址信息
(3)子类可以通过重写toString方法的形式,改变输出的内容以及表现形式
在测试类中
public class TestOne { public static void main(String[] args){ Animal one=new Animal("小one",2); System.out.println(one.toString()); System.out.println(one); } } //运行结果 Animal@26cbb7db Animal@26cbb7db
在Animal类中重写toString方法
public class Animal { private String name; private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Animal(String name,int age){ this.name=name; this.age=age; System.out.println(name+age+"岁"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString(){ //重写toString方法 return "昵称:"+this.getName()+";年龄:"+this.getAge(); } } //运行结果 小one2岁 昵称:小one;年龄:2 昵称:小one;年龄:2