1、继承
在java中,所有的类都直接或间接继承Object类,Object类称为所有Java类的祖先。java中的继承是通过extends关键字操作的。通常说被继承的类叫作父类,派生的类叫做子类,通过继承后,子类可以使用父类的成员变量和成员方法。由此特点,通常在生产中把父类设计为拥有所有子类的共同属性和行为的类,即多个类中相同的内容给提取出来定义到一个类。比如人会吃饭和睡觉,那么学生和老师也会吃饭和睡觉,如下
| class Person { public void sleep() { System.out.println("人会睡觉的!"); } public void eat() { System.out.println("人会吃饭的!"); } } //继承格式:class 子类名 extends 父类名 {} class Student extends Person{} //Student类继承了Person类 class Teacher extends Person{} //Teacher类继承了Person类 class test { public static void main(String[] args) { Student s1 = new Student(); s1.sleep(); //Student类对象s1调用父类的sleep()成员方法 s1.eat(); System.out.println("---------"); Teacher t1 = new Teacher(); t1.sleep(); //Teacher类对象t1调用父类的sleep()成员方法 t1.eat(); } } //执行结果如下: 人会睡觉的! 人会吃饭的! --------- 人会睡觉的! 人会吃饭的! |
1.1、 Java中继承的特点:
| A:Java只支持单继承,不支持多继承。 //class Son extends Father,Mother {} // java这样的多继承是错误的 但有些语言是支持多继承,格式:extends 类1,类2,... B:Java支持多层继承(继承体系) class GrandFather(){} class Father extends GrandFather(){} class Son extends Father(){} |
| java多层继承的案例 |
| class GrandFather { public void show() { System.out.println("我是爷爷"); } } class Father extends GrandFather { public void method(){ System.out.println("我是老子"); } } class Son extends Father {} class ExtendsDemo2 { public static void main(String[] args) { Son s = new Son(); s.method(); //使用父亲的 s.show(); //使用爷爷的 } } |
1.2、继承注意事项
A:子类只能继承父类所有非私有的成员(即成员方法和成员变量) B:子类不能继承父类的构造方法,但是可以通过super(马上讲)关键字去访问父类构造方法。(子类通过super访问父类的成员方法) C:不要为了部分功能而去继承。只要A和B两者关系是"A is B"或"B is A"才去继承。 |
| 继承的案例2: |
| class Father { private int num = 10; public int num2 = 20; //私有方法,子类不能继承 private void method() { System.out.println(num); System.out.println(num2); } public void show() { System.out.println(num); System.out.println(num2); } } class Son extends Father { public void function() { //num可以在Father中访问private //System.out.println(num); //子类不能继承父类的私有成员变量 System.out.println(num2); } } class ExtendsDemo3 { public static void main(String[] args) { // 创建对象 Son s = new Son(); //s.method(); //子类不能继承父类的私有成员方法 s.show(); s.function(); } } |
1.3、继承中成员变量和成员方法的调用
| 继承中成员变量的调用规则 |
| class Father { public int num = 10; public void method() { int num = 50; } } class Son extends Father { public int num2 = 20; public int num = 30; public void show() { int num = 40; System.out.println(num); System.out.println(num2); // 找不到符号,报错 //System.out.println(num3); } } class ExtendsDemo4 { public static void main(String[] args) { //创建对象 Son s = new Son(); s.show(); } } |
| 继承中成员方法的调用规则: |
| class Father { public void show() { System.out.println("show Father"); } } class Son extends Father { public void method() { System.out.println("method Son"); } public void show() { System.out.println("show Son"); } } class ExtendsDemo8 { public static void main(String[] args) { //创建对象 Son s = new Son(); s.show(); s.method(); //s.fucntion(); //找不到符号 } } |
由以上两个案例可知,在继承中,无论是成员变量还是成员方法,调用顺序都遵守“就近原则”:
| 成员变量:子类方法局部变量-->子类成员变量-->父类成员变量 |
| 成员方法:子类成员方法-->父类成员方法 |
1.4、继承中this和super关键字的作用
| this和super的区别? |
| this代表本类对应的引用。 super代表父类存储空间的标识(可以理解为父类的引用,可以操作父类的成员) |
怎么用呢? A:调用成员变量 this.成员变量 调用本类的成员变量 super.成员变量 调用父类的成员变量 B:调用构造方法 this(...) 调用本类的构造方法 super(...) 调用父类的构造方法 C:调用成员方法 this.成员方法 调用本类的成员方法 super.成员方法 调用父类的成员方法 |
| 继承中用this和super区分父类(成员变量)、子类(成员变量)和子类方法中(局部变量)同名num的值 |
| class Father { public int num = 10; } class Son extends Father { public int num = 20; public void show() { int num = 30; System.out.println(num); //局部变量优先级最高,num=30 System.out.println(this.num); //本类成员变量num=20 System.out.println(super.num); //父类成员变量num=30 } } class ExtendsDemo5 { public static void main(String[] args) { Son s = new Son(); s.show(); } } |
1.5、继承的构造方法
在继承中,子类中所有的构造方法默认都会访问父类中无参构造方法(因为子类会继承父类中的数据,可能还会使用父类的数据。)所以,子类初始化之前,一定要先完成父类数据的初始化。
| 注意:子类每一个构造方法的第一条语句默认都是:super(); 如下案例: |
| class Father { int age; public Father() { System.out.println("Father的无参构造方法"); } public Father(String name) { System.out.println("Father的带参构造方法"); } } class Son extends Father { public Son() { //super(); System.out.println("Son的无参构造方法"); } public Son(String name) { //super(); System.out.println("Son的带参构造方法"); } } class ExtendsDemo6 { public static void main(String[] args) { //创建对象 Son s = new Son(); System.out.println("------------"); Son s2 = new Son("林青霞"); } } //执行结果如下: Father的无参构造方法 Son的无参构造方法 ------------ Father的无参构造方法 Son的带参构造方法 |
特别地,如果父类没有无参构造方法的话,那么子类的构造方法就会出现报错。解决方式如下:
| 解决父类没有无参构造方法的措施: A:在父类中加一个无参构造方法 B:通过使用super关键字去显示的调用父类的带参构造方法 C:子类通过this去调用本类的其他构造方法 子类中一定要有一个去访问了父类的构造方法,否则父类数据就没有初始化。 注意事项: this(...)或者super(...)必须出现在第一条语句上。 如果不是放在第一条语句上,就可能对父类的数据进行了多次初始化,所以必须放在第一条语句上。 |
| class Father { public Father(String name) { System.out.println("Father的带参构造方法"); } } class Son extends Father { public Son() { super("随便给"); //super调用父类的带参构造方法 System.out.println("Son的无参构造方法"); //super("随便给"); } public Son(String name) { //super("随便给"); this(); //通过this调用本类的无参son()构造方法中的super("随便给");调用父类的带参构造方法初始化数据 System.out.println("Son的带参构造方法"); } } class ExtendsDemo7 { public static void main(String[] args) { Son s = new Son(); System.out.println("----------------"); Son ss = new Son("林青霞"); } } //执行结果如下: Father的带参构造方法 Son的无参构造方法 ---------------- Father的带参构造方法 Son的无参构造方法 Son的带参构造方法 |
1.6、三道经典的继承案例
| 看程序写结果案例1: |
| class Fu { static { System.out.println("静态代码块Fu"); } { System.out.println("构造代码块Fu"); } public Fu() { System.out.println("构造方法Fu"); } } class Zi extends Fu { static { System.out.println("静态代码块Zi"); } { System.out.println("构造代码块Zi"); } public Zi() { System.out.println("构造方法Zi"); } } class ExtendsTest2 { public static void main(String[] args) { Zi z = new Zi(); } } //执行的结果是: 静态代码块Fu 静态代码块Zi 构造代码块Fu 构造方法Fu 构造代码块Zi 构造方法Zi |
| super的秒用:案例2 |
| class Person { //姓名 private String name; //年龄 private int age; public Person() { } public Person(String name,int age) { //"林青霞",27 this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } //定义学生类 class Student extends Person { public Student() {} public Student(String name,int age) { //"林青霞",27 //this.name = name; //this.age = age; super(name,age); //super调用父类的this.name = name和this.age = age; } } //定义老师类 class Teacher extends Person { } class ExtendsTest4 { public static void main(String[] args) { //创建学生对象并测试 //方式1 Student s1 = new Student(); s1.setName("林青霞"); s1.setAge(27); System.out.println(s1.getName()+"---"+s1.getAge()); //方式2 Student s2 = new Student("林青霞",27); System.out.println(s2.getName()+"---"+s2.getAge()); //补齐老师类中的代码并进行测试。 } } |
| 继承面试题:案例3 |
| //看程序写结果 class X { { System.out.print("Father"); } Y b = new Y(); X() { System.out.print("X"); } } class Y { Y() { System.out.print("Y"); } } public class test extends X { { System.out.print("Son"); } Y y = new Y(); test() { //super System.out.print("Z"); } public static void main(String[] args) { new test(); } } //执行结果如下 FatherYXSonYZ //由上结果可知,继承中,子类new对象初始化时,是直接去父类中进行数据初始化后,再进子类的进行数据初始化的。 |
2、方法重写
| 方法重写Override | 方法重载Overload |
| 方法重写:子类中出现了和父类中方法声明一模一样的方法。 | 方法重载: 本类中出现的方法名一样,但参数列表和返回值类型都不同。 |
| class Phone { public void call(String name) { System.out.println("给"+name+"打电话"); } } class NewPhone extends Phone { public void call(String name) { super.call(name); System.out.println("可以听天气预报了"); } } class ExtendsDemo9 { public static void main(String[] args) { NewPhone np = new NewPhone(); np.call("苏炳添"); } } | public class test { public static void main(String[] args){ System.out.println(sum('a','b')); System.out.println(sum(3,3,3)); System.out.println(sum(3,3,3,3)); } //two char public static char sum(char a,char b) { return (char) (a+b); } //three number public static int sum(int a, int b, int c) { return a + b + c ; } //four number public static int sum(int a, int b, int c,int d) { return a + b + c + d; } } |
2.1、方法重写的特点
| (1)子类对象调用方法的时候: 先找子类本身,再找父类。 (2)方法重写的应用: 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法。 这样,即沿袭了父类的功能,又定义了子类特有的内容。 (3)方法重写的注意事项 A:父类中私有方法不能被重写 因为父类私有方法子类根本就无法继承 B:子类重写父类方法时,访问权限不能更低 最好就一致 C:父类静态方法,子类也必须通过静态方法进行重写 其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中我会讲解 //子类重写父类方法的时候,最好声明一模一样。 |
3、final关键字
final字面意思表“最终的”,它可以修饰类,方法,变量。
3.1、final的特点
| final的特点: |
final可以修饰类,该类不能被继承。 final可以修饰方法,该方法不能被重写。(覆盖,复写) final可以修饰变量,该变量不能被重新赋值。因为这个变量其实常量。 |
3.2、final修饰局部变量
| final修饰局部变量的问题? 基本类型:基本类型的值不能发生改变。 引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。 |
class Student { int age = 10; } class FinalTest { public static void main(String[] args) { //局部变量是基本数据类型 int x = 10; x = 100; System.out.println(x); final int y = 10; //无法为最终变量y分配值 //y = 100; System.out.println(y); System.out.println("--------------"); //局部变量是引用数据类型 Student s = new Student(); System.out.println(s.age); s.age = 100; System.out.println(s.age); System.out.println("--------------"); final Student ss = new Student(); System.out.println(ss.age); ss.age = 100; System.out.println(ss.age); //重新分配内存空间 //无法为最终变量ss分配值 ss = new Student(); } } |
3.3、final修饰变量的初始化时机
| final修饰变量的初始化时机 A:被final修饰的变量只能赋值一次。 B:在构造方法完毕前。(非静态的常量) //就是说final的变量赋值的时机要在代码块里赋值,或在构造方法中赋值,又或者在声明final变量时直接赋值才行。 赋值的顺序:成员方法声明>代码块>成员方法 |
| class Demo { //int num = 10; //final int num2 = 20; int num; final int num2; //final变量赋值点1 { //num2 = 10; //final变量赋值点2 } public Demo() { num = 100; //无法为最终变量num2分配值 num2 = 200; //final变量赋值点2 } } class FinalTest2 { public static void main(String[] args) { Demo d = new Demo(); System.out.println(d.num); System.out.println(d.num2); } } |
3.4、final保护父类的机密性
| final保护父类的机密性: |
| class Fu { public final void show() { System.out.println("这里是绝密资源,任何人都不能修改"); } } class Zi extends Fu { // Zi中的show()无法覆盖Fu中的show() public void show() { System.out.println("这是一堆垃圾"); } } class ZiDemo { public static void main(String[] args) { Zi z = new Zi(); z.show(); } } |
4、多态
多态:同一个对象(事物),在不同时刻体现出来的不同状态。比如水在不同的条件下可以有液体、气体和固体三个状态。
4.1、多态的条件
多态的前提: A:要有继承关系。 B:要有方法重写。 其实没有也是可以的,但是如果没有这个就没有意义。 动物 d = new 猫(); d.show(); 动物 d = new 狗(); d.show(); C:要有父类引用指向子类对象。 父 f = new 子(); |
4.2、 多态中的成员访问特点:
多态中的成员访问特点: A:成员变量 编译看左边(父类),运行看左边(父类)。 B:构造方法 创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化。 C:成员方法 编译看左边(父类),运行看右边(子类)。 D:静态方法 编译看左边(父类),运行看左边(父类)。 (静态和类相关,算不上重写,所以,访问还是左边的) 由于成员方法存在方法重写,所以它运行看右边。 |
| class Fu { public int num = 100; public void show() { System.out.println("show Fu"); } public static void function() { System.out.println("function Fu"); } } class Zi extends Fu { public int num = 1000; public int num2 = 200; public void show() { System.out.println("show Zi"); } public void method() { System.out.println("method zi"); } public static void function() { System.out.println("function Zi"); } } class DuoTaiDemo { public static void main(String[] args) { //要有父类引用指向子类对象。 //父 f = new 子(); Fu f = new Zi(); System.out.println(f.num); //找不到符号 //System.out.println(f.num2); f.show(); //找不到符号 //f.method(); f.function(); } } |
4.3、多态的向上转型和向下转型:
| 多态的向上转型和向下转型: |
| 多态的弊端: 不能使用子类的特有功能。 我就想使用子类的特有功能?行不行? 行。 怎么用呢? A:创建子类对象调用方法即可。(可以,但是很多时候不合理。而且,太占内存了) B:把父类的引用强制转换为子类的引用。(向下转型) 对象间的转型问题: 向上转型: Fu f = new Zi(); 向下转型: Zi z = (Zi)f; //要求该f必须是能够转换为Zi的。 |
| //案例代码如下: class Fu { public void show() { System.out.println("show fu"); } } class Zi extends Fu { public void show() { System.out.println("show zi"); } public void method() { System.out.println("method zi"); } } class DuoTaiDemo4 { public static void main(String[] args) { //测试 Fu f = new Zi(); f.show(); //f.method(); //创建子类对象 //Zi z = new Zi(); //z.show(); //z.method(); //你能够把子的对象赋值给父亲,那么我能不能把父的引用赋值给子的引用呢? //如果可以,但是如下 Zi z = (Zi)f; z.show(); z.method(); } } |
4.4、多态的内存图
4.5、多态的经典案例
| 多态的经典案例: |
| class A { public void show() { show2(); } public void show2() { System.out.println("我"); } } class B extends A { public void show2() { System.out.println("爱"); } } class C extends B { public void show() { super.show(); } public void show2() { System.out.println("你"); } } public class DuoTaiTest4 { public static void main(String[] args) { A a = new B(); a.show(); B b = new C(); b.show(); } } //执行结果如下: 爱 你 |
5、抽象类
在java中用abstract关键字来声明抽象类,抽象类是不能实例化的类,是泛指的概念,需要子类去实例化。比如动物可以设计成一个抽象的类,要用猫类和狗类去实例化。吃饭和睡觉也可以设置成抽象类中抽象的方法,再通过不同的子类(猫类或狗类)去实现各自具体不同的动作。这样通过一个抽象类,由子类去实现具体的功能,提高了代码的扩展性(子类保证)和易维护(抽象的父类保护)。
5.1、抽象类的基本特点
| 抽象类的基本特点 |
| A:抽象类和抽象方法必须用abstract关键字修饰 B:抽象类中不一定有抽象方法,但是有抽象方法的类必须定义为抽象类 C:抽象类不能实例化 因为它不是具体的。 抽象类有构造方法。构造方法的作用是什么呢?用于子类访问父类数据的初始化。 D:抽象的子类 a:如果不想重写抽象方法,该子类是一个抽象类。 b:重写所有的抽象方法,这个时候子类是一个具体的类。 抽象类的实例化其实是靠具体的子类实现的。是多态的方式。 Animal a = new Cat(); 同时 abstract不能与private、final和static关键字共存。 |
| //abstract class Animal //抽象类的声明格式 abstract class Animal { //抽象方法 //public abstract void eat(){} //空方法体,这个会报错。抽象方法不能有主体 public abstract void eat(); public Animal(){} } //子类是抽象类 abstract class Dog extends Animal {} //子类是具体类,重写抽象方法 class Cat extends Animal { public void eat() { System.out.println("猫吃鱼"); } } class AbstractDemo { public static void main(String[] args) { //创建对象 //Animal是抽象的; 无法实例化 //Animal a = new Animal(); //通过多态的方式 Animal a = new Cat(); a.eat(); } } |
5.2、抽象类的成员特征
抽象类的成员特点: 成员变量:既可以是变量,也可以是常量。 构造方法:有。 用于子类访问父类数据的初始化。 成员方法:既可以是抽象的,也可以是非抽象的。 抽象类的成员方法特性: A:抽象方法 强制要求子类做的事情。 B:非抽象方法 子类继承的事情,提高代码复用性。 |
| abstract class Animal { public int num = 10; public final int num2 = 20; public Animal() {} public Animal(String name,int age){} public abstract void show(); public void method() { System.out.println("method"); } } class Dog extends Animal { public void show() { System.out.println("show Dog"); } } class AbstractDemo2 { public static void main(String[] args) { //创建对象 Animal a = new Dog(); a.num = 100; System.out.println(a.num); //a.num2 = 200; System.out.println(a.num2); System.out.println("--------------"); a.show(); a.method(); } } |
5.3、经典抽象类(猫狗案例)
| //定义抽象的动物类 abstract class Animal { //姓名 private String name; //年龄 private int age; public Animal() {} public Animal(String name,int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } //定义一个抽象方法 public abstract void eat(); } //定义具体的狗类 class Dog extends Animal { public Dog() {} public Dog(String name,int age) { super(name,age); } public void eat() { System.out.println("狗吃肉"); } } //定义具体的猫类 class Cat extends Animal { public Cat() {} public Cat(String name,int age) { super(name,age); } public void eat() { System.out.println("猫吃鱼"); } } //测试类 class AbstractTest { public static void main(String[] args) { //测试狗类 //具体类用法 //方式1: Dog d = new Dog(); d.setName("旺财"); d.setAge(3); System.out.println(d.getName()+"---"+d.getAge()); d.eat(); //方式2: Dog d2 = new Dog("旺财",3); System.out.println(d2.getName()+"---"+d2.getAge()); d2.eat(); System.out.println("---------------------------"); Animal a = new Dog(); a.setName("旺财"); a.setAge(3); System.out.println(a.getName()+"---"+a.getAge()); a.eat(); Animal a2 = new Dog("旺财",3); System.out.println(a2.getName()+"---"+a2.getAge()); a2.eat(); //练习:测试猫类 } } |
6、接口