继承
1.1 类和类之间的常见关系。
1:既然继承是描述类和类之间的关系,就需要先来了解类和类之间的常见关系
1.1.1 现实生活的整体与部分
举例说明
1:现实生活
1:学生 是人
2:狗 是动物
3:球队 包含 球员 整体与部分的关系,部分可以删除和增加
4:笔记本包含 cpu 整体与部分的关系,部分不可以删除和增加
5:航母编队 包含(航母 护卫舰 驱逐舰 舰载机 核潜艇)
1.1.2 java中的类与类关系
java中的类关系
1:is a 关系 (学生是人)
2:has a 整体与部分
1 class Person{ 2 String name; 3 int age; 4 Address add; 5 6 Person(){ 7 8 } 9 Person(String name,int age,Address add){ 10 11 this.name=name; 12 this.age=age; 13 this.add=add; 14 15 } 16 17 void speak(){ 18 System.out.println("姓名:"+name+" 年龄:"+age+" "+add.print()); 19 } 20 } 21 class Address{ 22 String country; 23 String city; 24 String street; 25 26 Address(){ 27 28 } 29 Address(String country,String city,String street){ 30 this.country=country; 31 this.city=city; 32 this.street=street; 33 } 34 35 String print(){ 36 return "地址:"+country+" "+"城市:"+city+" 街道;"+street; 37 } 38 } 39 class Demo3{ 40 41 public static void main(String[] args){ 42 43 Address add=new Address("中国","广州","棠东东路"); 44 Person p=new Person("jack",27,add); 45 p.speak(); 46 47 48 System.out.println(); 49 } 50 } 51
继承
1:描述一个学生类
1:姓名年龄学号属性,学习的方法
2:描述一个工人类
1:姓名年龄工号属性,工作的方法
3:描述一个人类
1:姓名年龄属性,说话的方法。
4:发现学生类和人类天生有着联系,学生和工人都是人。所以人有的属性和行为学生和工人都会有。出现类代码重复
1 class Person { 2 String name; 3 int age; 4 5 // 静态变量(类变量)对象和对象之间的代码重复使用静态变量 6 static String country = "CN"; 7 8 Person() { 9 10 } 11 12 void speak() { 13 System.out.println(name + ":哈哈,我是人!!!"); 14 } 15 16 } 17 18 // 让学生类和人类产生关系,发现学生is a 人,就可以使用继承 19 class Student { 20 21 String name; 22 int age; 23 24 Student() { 25 26 } 27 28 void study() { 29 System.out.println("姓名:" + name + "年纪:" + age + ":好好学习"); 30 } 31 } 32 33 class Worker { 34 String name; 35 int age; 36 37 void work() { 38 System.out.println(name + ":好好工作,好好挣钱。"); 39 } 40 41 } 42 43 class Demo1 { 44 45 public static void main(String[] args) { 46 Student s = new Student(); 47 s.name = "jack"; 48 s.age = 20; 49 s.study(); 50 51 Worker w = new Worker(); 52 w.name = "rose"; 53 54 w.work(); 55 } 56 }
5:问题:
1:如果没有继承,出现类和类的关系无法描述
2:如果没有继承,类和类之间有关系会出现类和类的描述代码的重复。
继承特点
1:描述类和类之间的关系
2:降低类和类之间的重复代码
1:降低对象和对象之间的代码重复使用静态变量
2:降低类和类之间的代码重复使用就继承
extends关键字
继承使用extends关键字实现
1:发现学生是人,工人是人。显然属于is a 的关系,is a就是继承。
2:谁继承谁?
学生继承人,发现学生里的成员变量,姓名和年龄,人里边也都进行了定义。有重 复代码将学生类的重复代码注释掉,创建学生类对象,仍然可以获取到注释的成员。这就是因为继承的关系,学生类(子类)继承了人类(父类)的部分
1 class Person { 2 String name; 3 int age; 4 5 // 静态变量(类变量)对象和对象之间的代码重复使用静态变量 6 static String country = "CN"; 7 8 Person() { 9 10 } 11 12 void speak() { 13 System.out.println(name + ":哈哈,我是人!!!"); 14 } 15 16 } 17 18 // 让学生类和人类产生关系,发现学生is a 人,就可以使用继承 19 class Student extends Person { 20 21 Student() { 22 23 } 24 25 void study() { 26 System.out.println("姓名:" + name + "年纪:" + age + ":好好学习"); 27 } 28 } 29 30 class Worker extends Person { 31 32 void work() { 33 System.out.println(name + ":好好工作,好好挣钱。"); 34 } 35 36 } 37 38 class Demo1 { 39 40 public static void main(String[] args) { 41 Student stu = new Student(); 42 stu.name = "jack"; 43 stu.age = 20; 44 stu.study(); 45 stu.speak(); 46 System.out.println(stu.country); 47 System.out.println(Student.country); 48 49 Worker worker = new Worker(); 50 worker.name = "rose"; 51 System.out.println(worker.country); 52 worker.work(); 53 worker.speak(); 54 55 System.out.println(); 56 } 57 }
继承细节;
1:类名的设定,被继承的类称之为父类(基类),继承的类称之为子类
2:子类并不能继承父类中所有的成员
1:父类定义完整的成员 静态成员,非静态,构造方法。静态变量和静态方
法都可以通过子类名.父类静态成员的形式调用成功。
2:所有的私有成员不能继承,private修饰的成员。
3:构造函数不能被继承
3:如何使用继承
1:不要为了使用继承而继承。工人和学生都有共性的成员,不要为了节省代
码,让工人继承学生。
1 /* 2 如何使用继承:验证是否有 is a 的关系 3 例如:学生是人, 小狗是动物 4 注意:不要为了使用某些功能而继承,java只支持单继承 5 */ 6 class DK { 7 8 void Ip4S() { 9 System.out.println("好玩"); 10 } 11 } 12 13 class BGir extends DK { 14 15 } 16 17 class Demo { 18 19 public static void main(String[] args) { 20 21 new BGir().Ip4S(); 22 23 } 24 }
super关键字
1:定义Father(父类)类
1:成员变量int x=1;
2:构造方法无参的和有参的,有输出语句
2:定义Son类extends Father类
1:成员变量int y=1;
2:构造方法无参和有参的。有输出语句
1:this.y=y+x;
3:创建Son类对象
Son son=new Son(3);
System.out.println(son.y); //4
1 class Father { 2 int x = 1; 3 4 Father() { 5 System.out.println("这是父类无参构造"); 6 } 7 8 Father(int x) { 9 10 this.x = x; 11 System.out.println("这是父类有参构造"); 12 } 13 14 void speak() { 15 System.out.println("我是父亲"); 16 } 17 } 18 19 class Son extends Father { 20 int y = 1; 21 22 Son() { 23 System.out.println("这是子类的无参构造"); 24 } 25 26 Son(int y) { 27 28 this.y = y + x; 29 System.out.println("这是子类的有参构造"); 30 } 31 32 void run() { 33 super.speak(); // 访问父类的函数 34 System.out.println("我是儿子"); 35 } 36 } 37 38 class Demo6 { 39 40 public static void main(String[] args) { 41 Son s = new Son(3); 42 System.out.println(s.y);// 4 43 } 44 }
4:子类对象为什么可以访问父类的成员。
1:this.y=y+x;有一个隐式的super super.x
5:super关键字作用
1:主要存在于子类方法中,用于指向子类对象中父类对象。
2:访问父类的属性
3:访问父类的函数
4:访问父类的构造函数
6:super注意
this和super很像,this指向的是当前对象的调用,super指向的是当前调用对象的父类。Demo类被加载,执行main方法,Son.class加载,发现有父类Father类,于是Father类也加载进内存。类加载完毕,创建对象,父类的构造方法会被调用(默认自动无参),然后执行子类相应构造创建了一个子类对象,该子类对象还包含了一个父类对象。该父类对象在子类对象内部。this super只能在有对象的前提下使用,不能在静态上下文使用。
子类的构造函数默认第一行会默认调用父类无参的构造函数,隐式语句
super();
1:父类无参构造函数不存在,编译报错。
1 Son(int y) { 2 //super();隐式语句 3 this.y = y + x; 4 System.out.println("这是子类的有参构造"); 5 }
子类显式调用父类构造函数
在子类构造函数第一行通过super关键字调用父类任何构造函数。如果显式调用父类构造函数,编译器自动添加的调用父类无参数的构造就消失。构造函数间的调用只能放在第一行,只能调用一次。super() 和this()不能同时存在构造函数第一行。
1 Son(int y) { 2 super(y);// 子类显式调用父类构造函数 3 this.y = y + x; 4 System.out.println("这是子类的有参构造"); 5 } 6 7 Son(int y) { 8 this(); //不能同时存在构造函数第一行 9 super(y); 10 this.y = y + x; 11 System.out.println("这是子类的有参构造"); 12 }
super思考
如果开发者自定义了一个类,没有显示的进行类的继承,那么该类中成员函数是否可以使用super关健健字?可以使用,继承了Object类,Object类是所有类的父类。
1 class Demo7 { 2 public void print(){ 3 System.out.println(super.toString()); 4 } 5 public static void main(String[] args){ 6 new Demo7().print(); 7 System.out.println(); 8 } 9 }
7:重写(Override)
1:定义Father类
1:姓名,吃饭方法,吃窝窝头。
2:定义Son类,继承Father
1:Son类中不定义任何成员,子类创建对象,仍然可以调用吃饭的方法。
2:父类的吃饭的方法,Son不愿吃。Son自己定义了吃饭的方法。
1:此时父类中有一个吃饭的方法,子类中有2个吃饭的方法,一模一样,只是方法体不一样。
2:一个类中两个函数一模一样,是不允许的。
1:编译运行,执行了子类的方法。
2:使用父类的方法,在子类方法中,使用super.父类方法名。
1 class Father { 2 String name; 3 4 void eat() { 5 System.out.println("吃窝窝"); 6 } 7 } 8 9 class Son extends Father { 10 11 public void eat() { // 继承可以使得子类增强父类的方法 12 System.out.println("来俩小菜"); 13 System.out.println("来两杯"); 14 System.out.println("吃香喝辣"); 15 System.out.println("来一根"); 16 } 17 } 18 19 class Demo8 { 20 21 public static void main(String[] args) { 22 Son s = new Son(); 23 //执行子类的方法 24 s.eat(); 25 26 } 27 }
3:该现象就叫做重写(覆盖 override)
1: 在继承中,子类可以定义和父类相同的名称且参数列表一致的函数,将这种函数
称之为函数的重写.
4:前提
1:必须要有继承关系
5:特点
1:当子类重写了父类的函数,那么子类的对象如果调用该函数,一定调用的是重写过后的函数。
可以通过super关键字进行父类的重写函数的调用。
2: 继承可以使得子类增强父类的方法
6:细节
1: 函数名必须相同
2:参数列表必须相同
3: 子类重写父类的函数的时候,函数的访问权限必须大于等于父类的函数的访
问权限否则编译报错
4:子类重写父类的函数的时候,返回值类型必须是父类函数的返回值类型或该返回值类型的子类。不能返回比父类更大的数据类型: 如子类函数返回值类型是Object
1:定义 A B C 类 B extends A
2:Father类中定义A getA();
3:Son 类中重写getA(); 方法,尝试将返回值修改为B,C ,Object
1:B编译通过
2:C 编译失败 ,没有继承关系
3:Object编译失败,比父类的返回值类型更大
1:定义 A B C 类 B extends A
2:Father类中定义A getA();
3:Son 类中重写getA(); 方法,尝试将返回值修改为B,C ,Object
1:B编译通过
2:C 编译失败 ,没有继承关系
3:Object编译失败,比父类的返回值类型更大
1 class A { 2 3 } 4 5 class B extends A { 6 7 } 8 9 class C { 10 11 } 12 class Father { 13 String name; 14 15 void eat() { 16 System.out.println("吃窝窝"); 17 } 18 19 // 定义一个函数,获取A类的对象, 20 A getA() { 21 return new A(); 22 } 23 24 } 25 26 class Son extends Father { 27 28 public void eat() { // 继承可以使得子类增强父类的方法 29 System.out.println("来两杯"); 30 System.out.println("来俩小菜"); 31 super.eat(); 32 System.out.println("来一根"); 33 } 34 35 // B类是A类的子类 36 B getA() { 37 return new B(); 38 } 39 } 40 41 class Demo8 { 42 43 public static void main(String[] args) { 44 Son s = new Son(); 45 s.eat(); 46 47 } 48 }
7:子类对象查找属性或方法时的顺序:
1:原则:就近原则。
如果子类的对象调用方法,默认先使用this进行查找,如果当前对象没有找到属性或方法,找当前对象中维护的super关键字指向的对象,如果还没有找到编译报错,找到直接调用。
8:重载和重写的不同
1:重载(overload):
1:前提: 所有的重载函数必须在同一个类中
2:特点:
函数名相同,参数列表不同,与其他的无关(访问控制符、返回值类型)
3:不同:
个数不同 、 顺序不同、 类型不同
2:重写(override):
1:前提: 继承
2:特点:
函数名必须相同、参数列表必须相同。
子类的返回值类型要等于或者小于父类的返回值
9:重写练习
描述不同的动物不同的叫法
1:定义动物类
有名字,有吃和叫的方法
2:定义狗继承动物重写父类吃和叫的方法
3:定义猫继承动物重写父类吃和叫的方法
1 class Animal{ 2 int x=1; 3 String name; 4 5 void eat(){ 6 System.out.println("吃东西"); 7 } 8 void shout(){ 9 System.out.println("我是动物"); 10 } 11 } 12 13 class Dog extends Animal{ 14 15 16 void eat(){ 17 System.out.println("啃骨头"); 18 } 19 void shout(){ 20 System.out.println("旺旺"); 21 } 22 void eat(String food){ 23 System.out.println("吃:"+food); 24 } 25 } 26 class Cat extends Animal{ 27 28 void eat(){ 29 System.out.println("吃老鼠"); 30 } 31 void shout(){ 32 System.out.println("喵喵"); 33 } 34 } 35 36 class Demo9{ 37 38 public static void main(String[] args){ 39 Dog d=new Dog(); 40 d.shout(); 41 d.eat(); 42 43 Cat c=new Cat(); 44 c.shout(); 45 c.eat(); 46 System.out.println(); 47 } 48 }
instanceof 关键字
1:快速演示instanceof
1 Person p=new Person(); 2 System.out.println( p instanceof Person);
2:instanceof是什么?
1:属于比较运算符:
2:instanceof关键字:该关键字用来判断一个对象是否是指定类的对象。
3:用法:
对象 instanceof 类;
该表达式是一个比较运算符,返回的结果是boolea类型 true|false
注意:使用instanceof关键字做判断时,两个类之间必须有关系。
3:案例
定义一个功能表函数,根据传递进来的对象的做不同的事情,如果是狗让其看家,如果是猫让其抓老鼠
1:定义动物类
2:定义狗类继承动物类
3:定义猫类继承动物类
4:定义功能根据传入的动物,执行具体的功能
5:instanceof好处
1:可以判断对象是否是某一个类的实例
1 package oop01; 2 3 /* 4 instanceof 5 比较运算符 6 检查是否是类的对象 7 1:可以判断对象是否是某一个类的实例 8 用法 9 对象 instanceof 类; 10 11 案例 12 定义一个功能函数,根据传递进来的对象的做不同的事情 13 如果是狗让其看家,如果是猫让其抓老鼠 14 1:定义动物类 15 2:定义狗类继承动物类 16 3:定义猫类继承动物类 17 4:定义功能根据传入的动物,执行具体的功能 18 */ 19 20 class Animal { 21 22 String name; 23 24 void eat() { 25 System.out.println("吃东西"); 26 } 27 28 void shout() { 29 System.out.println("我是动物"); 30 } 31 } 32 33 class Dog extends Animal { 34 35 void eat() { 36 System.out.println("啃骨头"); 37 } 38 39 void shout() { 40 System.out.println("旺旺"); 41 } 42 43 } 44 45 class Cat extends Animal { 46 47 void eat() { 48 System.out.println("吃老鼠"); 49 } 50 51 void shout() { 52 System.out.println("喵喵"); 53 } 54 } 55 56 class Demo11 { 57 58 public static void main(String[] args) { 59 60 Demo11 d = new Demo11(); 61 62 // 对象 instanceof 类; 63 System.out.println(d instanceof Demo11); 64 65 d.doSomething(new Dog()); 66 d.doSomething(new Cat()); 67 } 68 69 // 定义一个功能函数,根据传递进来的对象的做不同的事情 70 // 如果是狗让其看家,如果是猫让其抓老鼠 71 // 对象 instanceof 类; 72 void doSomething(Animal a) { 73 if (a instanceof Dog) { 74 a.eat(); 75 a.shout(); 76 System.out.println("小狗看家"); 77 } else if (a instanceof Cat) { 78 a.eat(); 79 a.shout(); 80 System.out.println("抓老鼠"); 81 } 82 } 83 }
1 byte[] bs = new byte[] { 1, 2, 3 }; 2 int[] is = new int[] { 1, 2, 3 }; 3 String[] ss = new String[] { "jack", "lucy", "lili" }; 4 System.out.println(bs instanceof byte[]); // true 5 System.out.println(is instanceof int[]); // true 6 System.out.println(ss instanceof String[]); // true 7 // System.out.println(bs instanceof int[]); // 不可转换的类型
final关键字
1:定义静态方法求圆的面积
2:定义静态方法求圆的周长
3:发现方法中有重复的代码,就是PI,圆周率。
1:如果需要提高计算精度,就需要修改每个方法中圆周率。
4:描述一个变量
1:方法都是静态的,静态只能访问静态,所以变量也定义为静态的。
public static double PI=3.14;
1:如果定义为public后,新的问题,类名.PI=300; 改变了PI的值。
2:修改为private,修改为private后进行了封装,需要getset公共访问方法。
3:现有的知识不能解决这样的问题了。可以使用final
1 class Demo12 { 2 3 public static final double PI = 3.14; // 静态常量 4 5 public static double getArea(double r) { 6 return PI * r * r; 7 } 8 9 public static double getLength(double r) { 10 return PI * r * 2; 11 } 12 13 public static void main(String[] args) { 14 15 // Demo12.PI=300; 无法为最终变量 PI 指定值 16 System.out.println(Demo12.PI); 17 18 } 19 20 }
5:使用final
1:final关键字主要用于修饰类、类成员、方法、以及方法的形参。
2:final修饰成员属性:
1:说明该成员属性是常量,不能被修改。
public static final double PI=3.14;
1:public :访问权限最大
2:static :内存中只有一份
3:final :是一个常量
4:常量名大写
5:必须初赋值。
2:使用类名.成员。修改该成员的值,报错。--常量不能被修改
1:基本数据类型,final使值不变
2:对象引用,final使其引用恒定不变,无法让其指向一个新的对象,但是对象自身却可以被修改。
3:该关键字一般和static关键字结合使用
1:常量可以优先加载,不必等到创建对象的时候再初始化。
4:final和static可以互换位置
5:常量一般被修饰为final
3:fianl修饰类:
1:该类是最终类,不能被继承。
1:将父类加final修饰,子类继承,就会报错。
2:查看api文档发现String类是final的。Integer类也是final的
1:为了防止代码功能被重写
2:该类没有必要进行扩展
4:final修饰方法:
1:该方法是最终方法,不能被重写
2:当一个类被继承,那么所有的非私有函数都将被继承,如果函数不想被子类继承并重写可以将该函数final修饰
3:当一个类中的函数都被修饰为final时,可以将类定义为final的。
1 class Father2{ 2 final void eat(){ 3 System.out.println("eating...."); 4 } 5 } 6 7 class Son2 extends Father2{ 8 //该方法是最终方法,不能被重写 9 void eat(){ 10 System.out.println("eating...."); 11 } 12 } 13 class Demo12 { 14 15 16 public static void main(String[] args) { 17 18 // Demo12.PI=300; 无法为最终变量 PI 指定值 19 System.out.println(Demo12.PI); 20 Son2 s=new Son2(); 21 s.eat(); 22 23 }
5:final关键字修饰形参
1:当形参被修饰为final,那么该形参所属的方法中不能被篡改。
2: 项目中主要用于一些只用来遍历未知数据的函数。将未知变量声明为final的。增强数据的安全性。
1 class Demo14 { 2 3 public static void main(String[] args) { 4 5 System.out.println(); 6 String[] arr = { "think in java", "java就业教程", "java核心技术" }; 7 8 print(arr); 9 10 } 11 12 // 该方法,打印书名。 13 public static void print(final String[] arr) { 14 //arr = null; ,无法重新赋值 15 16 for (int x = 0; x < arr.length; x++) { 17 System.out.println(arr[x]); 18 } 19 } 20 21 }
10:思考
为什么子类一定要访问父类的构造函数呢
1:子类继承了父类的属性,如果要使用父类的属性必须初始化,创建子类对象,必须先初始化父类属性
必须调用父类的构造方法。
2:为什么调用父类无参的构造函数
设计java语言之时,只知道编译器会默认添加无参的构造函数,有参的无法确定。
但是可以通过super关键字显式调用父类指定构造函数。
3:为什么super()this()语句要放在构造函数的第一行
子类可能会用到父类的属性,所以必须先初始化父类。