java继承
1.方法继承
animal类
package com.imooc.animal; public class Animal { protected String name; //名字 private int month; //月份 private String species; //品种 //如果这里没有定义构造方法,编译器会自动生成 public Animal(){} public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public String getSpecies() { return species; } public void setSpecies(String species) { this.species = species; } public void eat(){ //子类只能继承父类的非私有成员,protected可以允许系内继承。 System.out.println(this.getName() + " eating"); } }
cat类
package com.imooc.animal; public class Dog extends Animal { private String sex; //性别 public Dog() {} public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } //睡觉 public void sleep(){ //以下几种打印方式都是可行的 System.out.println(this.getName() + " sleep"); // System.out.println(getName() + " sleep"); //下面方式必须是属性可继承。public/protected // System.out.println(super.name + " sleep"); // System.out.println(this.name + " sleep"); // System.out.println(name + " sleep"); //使用this.name和name都可以的原因,编译器会寻找这个 //现在本实例空间内寻找,然后去父类空间寻找 } }
Dog类
package com.imooc.animal; public class Cat extends Animal { private double weight; //体重 public Cat() {} public double getWeight() { return weight; } public void setWeight(double weight) { this.weight = weight; } //跑动方法 public void run() { System.out.println(this.getName()+" Running"); } }
Test测试方法
package com.imooc.test; import com.imooc.animal.Cat; import com.imooc.animal.Dog; public class Test { public static void main(String[] args) { Cat one = new Cat(); one.setName("花花"); one.setSpecies("中华田园猫"); one.eat(); one.run(); System.out.println("---------------------------------"); Dog two = new Dog(); two.setName("妞妞"); two.setMonth(1); two.eat(); two.sleep(); } }
2.方法的重写
animal类
package com.imooc.animal; public class Animal { protected String name; //名字 private int month; //月份 private String species; //品种 public int temp = 15; //如果这里没有定义构造方法,编译器会自动生成 public Animal(){} public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public String getSpecies() { return species; } public void setSpecies(String species) { this.species = species; } public void eat(){ //子类只能继承父类的非私有成员,protected可以允许系内继承。 System.out.println(this.getName() + " eating"); } public void eat(String name){ System.out.println(name + " eating"); } }
Dog类
package com.imooc.animal; public class Dog extends Animal { private String sex; //性别 // 属性值也是可以重写的,不过必须是public的访问类型 public int temp = 300; public Dog() {} public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } //睡觉 public void sleep(){ System.out.println(this.getName() + " sleep"); } /* * 方法重载 * 1、同一个类中 * 2、方法名相同,参数列表不同(参数顺序、个数、类型) * 3、方法返回值、访问修饰符任意 * 4、与方法的参数名无关 * */ private String sleep(String name) { return name; } /* * 方法重写(覆写) * 1、有继承关系的子类中 * 2、方法名相同,参数列表相同(参数顺序、个数、类型),方法返回值相同 * 3、访问修饰符,访问范围需要大于等于父类的访问范围 * 4、与参数名无关 * */ public void eat(){ System.out.println(this.name + " 最近没有食欲,Dog重写"); } //方法重写,与参数名无关,与参数顺序、个数、类型有关 public void eat(String dogName) { System.out.println(dogName + "Dog被重写"); } }
3.访问修饰符
Java中的访问修饰符主要有以下几类:
• 公有的:public 允许在任意位置访问
• 私有的:private 只允许在本类中进行访问
• 受保护的:protected 允许在当前类、同包子类/非子类、跨包子类调用、跨包非子类不允许
• 默认:允许在当前类、同包子类调用;跨包子类不允许调用
访问修饰符的作用域范围:
4.super关键字——调用父类的方法和属性
当子类继承父类的时候,如果子类将一个方法进行重写了,
那么在调用的时候,是调用子类的方法还是调用的父类的方法了?
animal类
package com.imooc.animal; public class Animal { protected String name; //名字 public Animal(){} public String getName() { return name; } public void setName(String name) { this.name = name; } //父类定义了eat方法 public void eat(){ System.out.println(this.getName() + " eating"); } public void eat(String name){ System.out.println(name + " eating"); } }
Dog类
package com.imooc.animal; public class Dog extends Animal { public Dog() {} public void eat(){ System.out.println(this.name + " 最近没有食欲,Dog重写"); } public void eat(String dogName) { System.out.println(dogName + "Dog被重写"); } public void sleep(){ eat(); } }
测试代码:
package com.imooc.test; import com.imooc.animal.Dog; public class Test { public static void main(String[] args) { Dog two = new Dog(); two.setName("妞妞"); two.eat(); //妞妞 最近没有食欲,Dog重写 } }
我们发现,默认会调用子类的方法,这也符合调用顺序。
那如果我就想调用父类的方法,该如何了?可以使用super关键字。
super.eat();
不仅是方法,父类中允许子类派生的属性,也可以通过super来继承。
父类的构造方法不允许被继承、不允许被重写。
5.继承的初始化顺序
(1)静态优先
(2)父类优先
(3)非静态块优于构造函数
写出下面代码的执行结果:
public class ExecutionSequence { public static void main(String[] args) { new GeneralClass(); } } class ParentClass{ //静态代码块 static { System.out.println("①我是父类静态块"); } //非静态块 { System.out.println("②我是父类非静态块"); } //父类构造方法 public ParentClass(){ System.out.println("③我是父类构造函数"); } } class GeneralClass extends ParentClass { //子类静态代码块 static { System.out.println("④我是子类静态块"); } { System.out.println("⑤我是子类非静态块"); } public GeneralClass(){ System.out.println("⑥我是子类构造函数"); } } /* ①我是父类静态块 ④我是子类静态块 ②我是父类非静态块 ③我是父类构造函数 ⑤我是子类非静态块 ⑥我是子类构造函数 */
为什么父类的构造函数在子类的非静态代码块之前?
“父类优先的原则”优于“非静态块优于构造函数的原则”。
6.super继承父类的构造方法
public class Animal { String name; //名字 } public class Cat extends Animal { private double weight; //体重 } public class Test { public static void main(String[] args) { Cat one = new Cat(); } }
如果都不定义给构造方法,那么编译器会默认创建无参构造
public class Animal { String name; //名字 public Animal(){ System.out.println("我是父类的无参构造,方法Animal"); } public Animal(String name){ this.name = name; System.out.println("我是父类的带参构造,方法名Animal,参数name"); } } public class Cat extends Animal { private double weight; //体重 public Cat(){ System.out.println("我是子类的无参构造,方法Cat"); } public Cat(String name,double weight) { this.name = name; this.weight = weight; System.out.println("我是子类的带参构造,方法名Cat,参数name、weight"); } }
情况一:
public class Test { public static void main(String[] args) { Cat one = new Cat(); } }
结果:
我是父类的无参构造,方法Animal
我是子类的无参构造,方法Cat
情况二:
public class Test { public static void main(String[] args) { Cat one = new Cat("ke",58); } }
结果:
我是父类的无参构造,方法Animal
我是子类的带参构造,方法名Cat,参数name、weight
虽然构造方法不允许被继承、被重写,但是会影响子类的构造。
子类构造默认调用父类的无参构造方法。
如果你重新定义了有参构造方法,那么构造的时候就必须带参,所以一般都需要写上无参构造。
如果子类希望调用父类的有参构造方法,可以使用super显式继承。
同时super()必须放在子类构造方法有效代码的第一行。
public class Cat extends Animal { private double weight; //体重 public Cat(){ System.out.println("我是子类的无参构造,方法Cat"); } public Cat(String name,double weight) { super(name); this.name = name; this.weight = weight; System.out.println("我是子类的带参构造,方法名Cat,参数name、weight"); } } public class Test { public static void main(String[] args) { Cat one = new Cat("mao",68); } }
结果:
我是父类的带参构造,方法名Animal,参数name
我是子类的带参构造,方法名Cat,参数name、weight
下面是一个常见问题:
public class Animal { String name; //名字 public Animal(String name){ this.name = name; System.out.println("我是父类的带参构造,方法名Animal,参数name"); } } public class Cat extends Animal { private double weight; //体重 public Cat(){ System.out.println("我是子类的无参构造,方法Cat"); //报错 } public Cat(String name,double weight) { this.name = name; this.weight = weight; System.out.println("我是子类的带参构造,方法名Cat,参数name、weight"); } }
上面会直接报错。
子类会默认调用父类的无参构造,但是父类只有有参构造,所以就会报错。
需要super()显式继承或者定义父类的无参构造。
super代表父类对象,this代表当前对象。
当然构造方法也是可以调用同类中的构造方法。
public class Cat extends Animal { private double weight; //体重 public Cat(){ System.out.println("我是子类的无参构造,方法Cat"); } public Cat(String name,double weight) { this(); this.name = name; this.weight = weight; System.out.println("我是子类的带参构造,方法名Cat,参数name、weight"); } }
结果:
我是子类的无参构造,方法Cat
我是子类的带参构造,方法名Cat,参数name、weight
既然super和this都可以调用构造方法,那么二者可以共存吗?
当你在调用构造方法的时候,super和this只能使用一个。
7.Object类
Object类是所有类的父类。
一个类没有使用extends关键字明确标识继承关系,则默认继承Object类(包括数组)
Java中的每个类都可以使用Object中定义的方法。
Object类官网:https://docs.oracle.com/javase/8/docs/api/
Object类中定义的方法:
上面都是Object类内置的方法,所以Java中所有的对象都可以调用这些方法。以equals为例:
下面是其源码,主要是比较两个对象是否是同一个对象。
public boolean equals(Object obj) { return (this == obj); }
示例:
public class Test { public static void main(String[] args) { Animal one = new Animal("mao"); Animal two = new Animal("mao"); //equals:比较两个引用是否指向同一个对象 boolean flag = one.equals(two); System.out.println("one和two的equals比较: "+flag); System.out.println("one和two的==比较:"+(one==two)); } } //one和two的equals比较: false //one和two的==比较:false
那如果,我们需要比较两个对象的值该怎么做了?我们可以重写equals方法
public boolean equals(Object obj){ if (obj == null){ return false; } Animal temp = (Animal) obj; //对象类型强制转换 if (this.getName().equals(temp.getName())){ return true; }else { return false; } }
上面这种写法,可以在类型转换的时候会报错,改进方式是直接传入Animal对象:
public boolean equals(Animal obj){ if (obj == null){ return false; } if (this.getName().equals(obj.getName())){ return true; }else { return false; } }
需要说明的是toString这个方法:
public class Test { public static void main(String[] args) { Animal one = new Animal("mao"); System.out.println(one); System.out.println(one.toString()); } } //com.imooc.animal.Animal@1b6d3586 //com.imooc.animal.Animal@1b6d3586
可以看出,输出对象名时,默认会直接调用类中的toString。
继承Object中的toString方法时,输出对象的字符串表示形式:类型信息+@+内存地址。
但是对字符串类型例外:
String str1 = new String("hello"); System.out.println(str1); //hello
很多时候我们会重写对象的toString方法,这样可以改变对象的默认打印。
8.final关键字
如果你希望一个类不被别人继承,那么可以定义final关键字:
public final class Animal { String name; //名字 public Animal(){} }
final class:该类没有子类
final 方法:该方法不允许被子类重写,但是可以被子类继承使用。
final 方法内局部变量:只要在具体被使用之前进行赋值即可,一旦赋值不允许被修改
final 类中成员属性:赋值过程:1.定义直接初始化 2.构造方法 3.构造代码块
可配合static使用,来定义只需要设置一次的内容