今日内容
1.继承(★)
1.1继承注意事项
注意事项 1)子类不能直接继承父类的私有化属性,可以间接访问 2)父类的构造方法是不能被继承的,只能间接访问---通过super
1.2继承中成员变量访问问题
成员变量访问问题 1)在继承中,子类的成员变量名和父类的成员变量名不一致,分别访问即可 2)如果子类的成员变量名和父类的成员变量名一致 就近原则机制 ---先在子类的局部位置(成员方法)中找,有则直接用 ---如果子类的局部位置没有,在子类的成员位置找,有则直接使用 ---如果还是没有,就在父类的成员位置中找,有则直接使用 ---还是没有就往父类的父类成员位置中找,有则直接使用 ---如此反复,若到顶部位置还是没有,则报错!
public class Fu {
//父类成员位置的num变量
int num = 10 ;
}
public class Zi extends Fu {
//子类成员变量位置的num变量
int num = 20 ;
public void show(){
//子类局部位置的num变量
int num = 30 ;
//此时,show方法被调用,输出哪个num?
System.out.println(num);
}
}
public class extendsTest {
public static void main(String[] args) {
Zi z = new Zi() ;
//会输出哪个num?
z.show();//30
}
}
1.3this和super区别
this 代表当前类对象的地址引用 this()---指向本类的无参构造方法 this(XX)---只想本类的有参构造方法 super 代表当前子类父类的空间标识 ---父类对象空间地址值引用 super()---指向父类的无参构造方法 super()---指向父类的有参构造方法
public class Fu {
int num = 10 ;
}
public class Zi extends Fu {
int num = 20 ;
public void show(){
int num = 30 ;
//就近原则
System.out.println(num);//30
//this---代表当前类对象的地址引用
System.out.println(this.num);//20
//super---代表父类对象空间地址值的引用
System.out.println(super.num);//10
}
}
public class extendsTest1 {
public static void main(String[] args) {
Zi z = new Zi() ;
z.show();
}
}
1.4构造方法与继承
构造方法与继承 构造方法的目的 类的成员进行数据初始化 子类不能继承父类的构造方法,但是可以通过super来间接访问---为什么?是不是多此一举 子类的所有构造方法,都默认访问父类的无参构造方法! ---因为在子类初始化时可能用到父类的数据,所以需要父类先初始化 ---所有子类构造方法第一句是super();可省略但不是没有!
1.4.1面试题
继承关系中, 父类的中如果没有无参构造方法(存在有参构造方法),子类会出现什么情况?如何解决呢?
分析 父类定义了的有参的构造方法,所以系统不会默认存在无参的构造方法 子类会怎么样? 子类会全部报错,子类的所有构造方法都默认指向父类无参的构造方法! 该如何解决 1)给父类添加无参构造方法 2)让所有子类的所有构造方法都指向父类有参的构造方法 3)子类的所有构造方法中只要有一个可以让父类初始化即可 执行本类的创建面向对象的无参构造方法时 --->先访问子类的有参构造方法---this(xx) --->再让有参构造方法---super(xx) --->访问父类的有参构造方法 子类的无参构造方法---访问父类的无参构造方法 子类的有参构造方法---访问父类的有参构造方法
//父类没有无参的构造方法public Fu(){}
public class Fu {
//父类有参构造方法,接收数据完成父类初始化!
public Fu(String name){
System.out.println("猜猜我是谁? 我是:"+name);
}
}
public class Zi extends Fu {
//子类的无参构造方法,通过this特性访问子类的有参构造方法
public Zi(){
this("钟离") ;
}
//子类的有参构造方法,通过super特性访问父类的有参构造方法,完成父类初始化!
public Zi(String name ){
super(name);
}
}
/*
父类没有无参的构造方法
*/
public class extendsTest2 {
public static void main(String[] args) {
//子类面向对象,先访问子类的无参构造方法
Zi z = new Zi() ;
}
}
1.5方法重写
方法重写 如果子类和父类的成员方法不一样,分别调用即可 如果子类的成员方法名和父类的成员方法相同,子类会将父类的方法进行覆盖 ---方法重写! ---继承中使用,子类出现了和父类一模一样的成员方法,目的就是为了将父类中方法重写(覆盖) 注意事项 子类的方法访问权限不能比父类低
public class Fu {
public Fu(){
}
public void show(){
System.out.println("父类会写java");
}
}
public class Zi extends Fu {
public void show(){
System.out.println("子类不仅会java,还会写python!");
}
public void show1(){
super.show();
}
}
public class overRideTest {
public static void main(String[] args) {
Zi z = new Zi() ;
z.show();
z.show1();
}
}
1.6final关键字
final关键字 子类继承父类时,父类一些方法不需要或者不能让子类重写,为了保证数据的安全性 ---java提供了final关键字 ---final状态修饰符 ---最终的,无法更改的
public class Fu {
public final void show(){
System.out.println("这是不想被重写的一句话!");
}
}
public class Zi extends Fu {
//public void show(){ //报错,显示父类内容不可被修改!
// System.out.println("你必须被我重写!");
//}
}
public class finalTest {
public static void main(String[] args) {
Zi z = new Zi() ;
z.show();
}
}
final关键字特点 1)当final修饰类时,被修饰的类不能被继承! 2)当final修饰成员变量时,被修饰的成员变量成为常量 ---只能被定义一次,之后不能被修改! 3)当final修饰方法时,被修饰的方法不能被重写!
//final class Fu { //子类报错,不能继承被final修饰的类
public class Fu{
int num = 10 ;
final int num1 = 20 ;
//num1 = 30 ;//报错,不能修改!
}
public class Zi extends Fu {
public void show(){
num = 50 ;
//num1 = 30 ;//报错,子类也不能修改!
System.out.println(num+", "+num1);
}
}
public class finalTest1 {
public static void main(String[] args) {
Zi z = new Zi() ;
z.num = 60 ;
System.out.println(z.num);
//z.num1 = 30 ;//报错,面向对象也不能修改!
z.show();
}
}
1.6.1面试题
final修饰基本数据类型和修饰引用类型区别?
分析 final修饰基本数据类型后,基本数据类型的值就不会再改变 ---不能被赋值 final修饰引用数据类型后,引用数据类型的空间地址值就不会再改变了 ---不能重新开辟内存空间---不能重新new
public class finalTest2 {
public static void main(String[] args) {
//final修饰基本数据类型
int num = 10 ;
num = 20 ;
System.out.println(num);//num结果成功被修改
final int num1 = 10 ;
//num1 = 20 ;//报错,不能被修改!
System.out.println(num1);//num1结果没有被修改
System.out.println("-------------------------------");
//final修饰引用数据类型
Test t = new Test() ;
System.out.println(t);//@1540e19d
//开辟新的内存空间,新的空间地址值
t = new Test() ;
//输出空间地址值,创建新的成功
System.out.println(t);//@677327b6
final Test t2 = new Test() ;
//t2 = new Test() ;//报错,不能改变空间地址值
System.out.println(t2);
}
}
2.多态(引入)(★)
多态 现实世界---一个事物的多种形态 java面向对象中 多态---一个类体现出内存的变化 ---能够体现事物的不同形态 前提条件 1)必须有继承关系---无继承,不多态 2)必须存在方法重写 3)必须存在父类引用指向子类对象 格式 父类名 对象名 = new 子类名() ;
父类名 对象名 = new 子类名() ; 多态成员访问特点 1)成员变量 编译看左,运行看左 2)成员方法 编译看左,运行看右---非静态的成员方法 ---静态的方法不算重写,可以直接类名调用! 3)构造方法 因为存在继承,构造方法在执行的时候,分层初始化,先让父类初始化,然后是子类构造初始化
多态的优势 1)提高代码的扩展性 2)提高代码的复用性和可维护性
多态的劣势 父类名 对象名 = new 子类名 --->向上转型,无法访问子类特有的功能 Fu f = new Fu() ; 解决方案 向下转型---前提是必须有向上转型! 将父类引用(对象名)强制转换成子类引用(对象名) ---类似于基本数据类型之间的强制转换类型 ---目标类型 变量名 = (目标类型)数据值 ; 格式 子类名 对象名 = (子类名)父类引用(对象名) Zi z = (Zi)f
public class Ainmal {
private String name ;
private int age ;
private String color ;
//封装
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 String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Ainmal(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public Ainmal() {
}
public void eat(){
System.out.println("动物都喜欢吃东西!");
}
public void sleep(){
System.out.println("动物不得不睡觉!");
}
}
public class Cat extends Ainmal {
public Cat(String name, int age, String color) {
super(name, age, color);
}
public Cat() {
}
//猫的特异功能
public void washface(){
System.out.println("猫洗脸");
}
//方法重载
public void eat(){
System.out.println("猫站着吃!");
}
public void sleep(){
System.out.println("猫站着睡!");
}
}
public class Dog extends Ainmal {
public Dog(String name, int age, String color) {
super(name, age, color);
}
public Dog() {
}
//狗特有的功能
public void LookDoor(){
System.out.println("狗看门");
}
//方法重写
public void eat(){
System.out.println("狗蘸着吃!");
}
public void sleep(){
System.out.println("狗不睡觉!");
}
}
//相当于创了一个调用Ainmal的类
public class AinmalTools {
//禁止通过创建面向对象来访问此类
private AinmalTools(){}
//提供一个公共的调用方法,被static修饰可通过类名调用!
public static void useAinmal(Ainmal a){
a.eat();
a.sleep();
}
}
public class duotaiTest {
public static void main(String[] args) {
//测试
//多态利用
System.out.println("第一种方式");
//父类名 对象名 = new 子类名() ;
Ainmal a = new Cat() ;
//调用的是Ainmal中的方法,但是堆内存中的是Cat
a.eat();//猫站着吃!
a.sleep();//猫站着睡!
Ainmal a1 = new Dog() ;
a1.eat();//狗蘸着吃!
a1.sleep();//狗不睡觉!
System.out.println("第二种更好的方式");
Ainmal a2 = new Cat() ;
//通过类名调用
AinmalTools.useAinmal(a2);
//输出结果:猫站着吃!
// 猫站着睡!
Ainmal a3 = new Dog() ;
//将指向狗堆内存的地址传过去
AinmalTools.useAinmal(a3);
//输出结果:狗蘸着吃!
// 狗不睡觉!
System.out.println("----------------------------");
//调用子类中的特有方法
//向下转型
//子类名 对象名 = (子类名)父类引用 ;(类比强制转换)
Cat c = (Cat) a2 ;//将a2的空间地址值转换为Cat内存的,强制转换!
//方法可以调用了
c.washface();//猫洗脸
Dog d = (Dog) a3 ;
d.LookDoor();//狗看门
}
}