java基础学习(五):对象中的封装、继承和多态
目录
- 一、封装
- 1. 访问器
- 2. 更改器
- 3. 扩展
- 4. javaBean类
- 二、继承
- 1. 什么是继承?
- 2. 如何继承?使用什么关键字?
- 3. 继承链(多级继承或多层继承)
- 4. 方法重写
- 5. supper
- 三、多态
- 强制类型转换
一、封装
对外隐藏(使用private
),外界调用时,可读可写,通过方法控制。
- 供外界访问的,叫访问器(getter)
get
- 供外界修改的,叫更改器(setter)
set
- 被
private
修饰意味着它只能在本类内部被直接访问,其他类不能对其进行访问,类似于对外界隐藏。
1. 访问器
示例1:只提供访问器,只可读取,不可修改
- 在Cat类中,
age
属性被private
修饰,age
属性只能在Cat类中访问,外界(Test类)想要访问,需要提供访问器 - 由于,只提供了访问器,那么,在Test类中获得了读取
age
值的公共方法,可以对其进行访问,但不能对其进行修改public class Cat {private static int age = 10;//访问器public static int getAge() {return age;} }
public class Test {public static void main(String[] args) {Cat cat = new Cat();//读取System.out.println(cat.getAge()); }//输出:10
2. 更改器
示例2:只提供更改器,只可修改,不可读取
- 在Cat类中,
age
属性被private
修饰,age
属性只能在Cat类中访问,外界(Test类)想要修改,需要提供更改器 - 由于,只提供了更改器,那么,在Test类中获得了修改
age
值的公共方法,可以对其进行修改,但不能对其进行读取public class Cat {private static int age = 10;//更改器public static void setAge(int age) {Cat.age = age;} }
public class Test {public static void main(String[] args) {Cat cat = new Cat();//修改cat.setAge(12);//由于只提供了修改器,未提供访问器,所有不能读取 }
示例3:同时提供更改器和访问器,即可修改,又可读取
- 在Cat类中,
age
属性被private
修饰,age
属性只能在Cat类中访问,外界(Test类)想要修改并访问,需要提供更改器和供访问器 - 那么,在Test类中即可读取
age
,又可能对其进行修改public class Cat {private static int age = 10;//访问器public static int getAge() {return age;}//更改器public static void setAge(int age) {Cat.age = age;} }
public class Test {public static void main(String[] args) {Cat cat = new Cat();//读取System.out.println("修改前读取:" + cat.getAge());//修改cat.setAge(12);System.out.println("修改后读取:" + cat.getAge()); }//输出:修改前读取:10// 修改后读取:12
3. 扩展
- 基本类型,不提供更改器,是不能进行修改的,因此,基本类型安全。
- 引用类型,不提供更改器,可以进行修改,因此,引用类型不安全。
- 引用类型中的
String
类型除外,它是安全的,String
本身是不可变数组。
那么,引用类型的数据,不提供更改器,如何修改?
例如:数组
- 返回为数组地址,将a指向数组地址(浅拷贝),那么a与arr指向的是同一空间,a进行修改,arr指向的数据也被修改。
public class Cat {private int[] arr = {5,7,4,2,0};public int[] getArr() {return arr;//返回数组地址} }
public class Test {public static void main(String[] args) {Cat cat = new Cat();int[] a = cat.getArr();a[0] = 10;a[1] = 100;System.out.println(Arrays.toString(a));System.out.println(Arrays.toString(cat.getArr()));} }//输出:[10, 100, 4, 2, 0]// [10, 100, 4, 2, 0]
- 既然,浅拷贝不行,那么,可以试着使用深拷贝,只把数值给你,不给地址。
public class Cat {private int[] arr = {5,7,4,2,0};public int[] getArr() {int[] brr = new int[arr.length];for(int i=0;i<arr.length;i++) {brr[i]=arr[i];}return brr;} }
- 输出结果:
[10, 100, 4, 2, 0]
[5, 7, 4, 2, 0]
4. javaBean类
一种定义类的规范。
类中的属性用private
修饰,对外隐藏,并对外提供更改器和访问器,用这样定义的类是javaBean类。
二、继承
1. 什么是继承?
- 继承是一种允许一个类(称为子类或派生类)基于另一个类(称为父类、基类或超类)来构建的机制。
- 核心思想:子类会自动获得父类得所有属性和方法,并可以在次基础上进行扩展,添加新的- 属性和方法,或者修改继续来得方法得行为。
- java中的类仅支持单继承。
- 单继承:一个子类只能有一个直接父类。
- 子类可以调用父类的方法,但父类不可以调用子类的方法。
2. 如何继承?使用什么关键字?
- 例如:有一个父类(Animal类),父类中有品种(variety)属性、年龄(age)属性、run()方法、eat()方法
public class Animal {String variety;int age;public void run() {System.out.println("酷跑");}public static void eat() {System.out.println("吃肉");} }
- 有一个子类(Cat类),要想继承父类,可使用
extends
关键字继承public class Cat extends Animal{…… }
- 使用测试类(Test类)来测试下Cat类是否正常得继承了Animal类
public class Test {public static void main(String[] args) {Cat cat = new Cat();//继承父类得属性cat.variety = "狸花猫";cat.age = 2;//继承父类得非静态方法cat.run();//继承父类得静态方法cat.eat();System.out.println("品种:"+cat.variety+","+"年龄:"+cat.age);} }//输出:酷跑// 吃肉// 品种:狸花猫,年龄:2
- 注意:
- 被
private
修饰,子类不能继承 - 被
finale
修饰,子类可以继承,但不能重写 - 将父类的方法重写了,但依然想调用父类的方法,可以使用
super()
- 被
3. 继承链(多级继承或多层继承)
- 父类上边有父类,在上边还有父类等,比如:孩子 -> 父亲 -> 祖父 -> 曾祖父…
- 上面举例使用的:
Cat
->Animal
->Object
,这就是一条继承链 - object提供各种各样的方法:
equals()
clone()
- 注意:继承链不是多继承,java不支持多继承。
示例:
- 继承链:
Cat
->Animal
->Earth
->Object
- 祖父类Earth类
public class Earth {public void affair() {System.out.println("事务");} }
- 父类Animal类,Animal类继承Earth类
public class Animal extends Earth{String variety;int age;public void run() {System.out.println("酷跑");}public static void eat() {System.out.println("吃肉");} }
- 子类Cat类,Cat类继承Animal类
- Cat类既可以调用父类中的各种属性和方法,也可以调用祖父类的各种属性和方法
public class Cat extends Animal{} }
- Cat类既可以调用父类中的各种属性和方法,也可以调用祖父类的各种属性和方法
- 测试类Test类
public class Test {public static void main(String[] args) {//父类继承祖父类Animal animal = new Animal();animal.affair();System.out.println("--------------");//子类继承父类//子类调用父类的属性和方法Cat cat = new Cat();cat.variety = "狸花猫";cat.age = 2;cat.run();cat.eat();//子类调用祖父类的方法cat.affair();System.out.println("品种:"+cat.variety+","+"年龄:"+cat.age);} } //输出:事务 //-------------- // 酷跑 // 吃肉 // 事务 // 品种:狸花猫,年龄:2
4. 方法重写
1)重写:发生在父子类中,是指在子类中重新定义从父类继承来的方法,以改变方法的行为
- 方法签名必须一致,方法名、参数列表、返回类型相同
- 访问权限不能比父类更严格
- 一般会加
@Override
注释
2)例如:
- 上面的Cat类,想要重写父类Animal类的run()方法
public class Cat extends Animal{//重写父类run()方法@Overridepublic void run() {System.out.println("轻盈得跑");} }
- 既然,在Cat类中重写了run()方法
(子类继承父类的方法,进行了重写,那么子类不能直接调用父类的方法了,只能调用自己的方法) - 那么,测试类Test类中:
cat.run();
不在是继承父类,而是调用的Cat类中重写的run()方法,输出:轻盈得跑
- 重写可以隔代,前提中间没有任何对它进行重写
- 例如:
- Cat类想要重写祖父类Earth类中的
affair()
方法 - 前提是:Animal类没有对Earth类中的
affair()
方法进行重写public class Cat extends Animal{//重写祖父类affair()@Overridepublic void affair() {System.out.println("动物");} }
- 在测试类中,
cat.affair();
不在是继承调用祖父类中的affair()
方法,而是,Cat类中重写的affair()
方法,输出结果为:动物
- Cat类想要重写祖父类Earth类中的
5. supper
如果,即对父类中的run()方法重写了,但又想调用父类的该方法
- 方法一: 可以使用
super
关键字 ->super.方法名()
- 在Cat类中使用
super.方法名()
super.run();
写在上面,则先输出父类,在输出子类;写在下面,则先输出子类,在输出父类public class Cat extends Animal{@Overridepublic void run() {super.run();System.out.println("轻盈得跑");} }
- 那么,测试类Test类中
cat.run();
会先输出父类字输出子类:输出结果为:酷跑 轻盈得跑
- 注意:
super
:静态方法属于类,不能用于静态方法- 多层继承中的
super
:super
永远指向直接父类,不能跨级调用
- 在Cat类中使用
- 方法二: 可以使用
super()
关键字 -> 调用父类构造方法- 在Cat类中调用父类构造方法
public class Cat extends Animal{public Cat() {super();System.out.println("轻盈得跑");} }
- 在Cat类中调用父类构造方法
super.方法名()
与super()
的区别:
方法 | 作用于 | 作用 | 位置 | 参数 |
---|---|---|---|---|
super.方法名() | 子类方法中 | 调用父类被重写的方法 | 在方法的任何地方 | |
super() | 子类构造函数中 | 调用父类构造函数 | 必须是构造函数中的第一句语句 | 可以有参或无参 |
三、多态
实现多态,要有三个条件:
- 要有继承:父类写的笼统写,子类写的具体实现
- 要有重写:子类对父类笼统的方法,重写去书写
- 父类的引用指向子类对象
示例:
- 父类Animal类
public class Animal {public void run() {System.out.println("动起来");} }
- 子类Cat类,继承父类,并重写run()方法
public class Cat extends Animal {@Overridepublic void run() {System.out.println("酷跑");} }
- 子类Bird类,继承父类,并重新run()方法
public class Bird extends Animal {@Overridepublic void run() {System.out.println("飞");} }
- 测试类Test
public class Test {public static void main(String[] args) {Animal animal;animal = new Cat();//多态,父类的引用指向子类animal.run();//输出:酷跑animal = new Bird();animal.run();//输出:飞} }
强制类型转换
- 基本类型:基本可以进行转换
基本类型是以bit形式存储,可以进行转换int a = 20; short b = (short)a;
- 引用类型:父类和子类的关系,父类转换为子类
Animal animal = new Animal(); Bird bird = (Bird)animal;
- 使用多态,不会报错,但是执行有问题
- 父类的引用指向子类Cat(),进行强制转换:想要将cat类型转换为bird类型
Animal animal2 = new Cat();//多态,父类的引用指向子类Cat(),在堆中开辟的是cat类型空间 Bird bird = (Bird)animal2;//想要将cat类型转换为bird类型,两个毫无关系,转换有问题 System.out.println(bird);
- 但是如果多态是父类的引用指向是Bird(),可以进行转换
Animal animal3 = new Bird(); Bird bird = (Bird)animal3;//转换没问题 System.out.println(bird);
- 多态是父类的引用指向孙子类NewBird()类,NewBird()类继承Bird()类
NewBird()
->Bird()
->Animal()
Animal animal4 = new NewBird(); Bird bird = (Bird)animal4;//转换没问题 System.out.println(bird);
- 因此,只有想要转换的类型是目标类型的子孙后代,转换才没问题
如何判断谁是谁的子孙后代呢?
可以使用instanceof
Animal animal4 = new NewBird();
Bird bird;
if (animal4 instanceof Bird) {bird = (Bird)animal4;System.out.println(bird);
}