一、封装
封装就是公开代码的对外接口,隐藏具体的实现。使用者不关心内部的具体实现步骤和方法,只要能够使用并操作即可。就像我们生活中的手机、电脑、电视、洗衣机等,我们不关心它的构造和工作原理,只要知道如何操作就可以。
1.封装的意义
- 保护或者防止代码被破坏
- 保护成员属性,不让改类以外的程序直接访问修改
- 隐藏方法细节,只留出开放的方法接口供其它程序使用
对方法实现者:类的成员属性只有本类中的方法才可以直接访问并修改。其它类访问成员属性和方法时,只能通过本类预留的公共方法来访问本类的成员属性和方法。
对方法调用者:不关心方法内部如何实现,只要能够使用类中的方法,能通过方法访问类中成员属性即可。
2.封装的实现
Java中通过访问限定修饰符来实现类的封装
访问范围 | private | 默认无修饰符 | protected | public |
---|---|---|---|---|
同一个包中的同一个类 | √ | √ | √ | √ |
同一个包中的不同类 | √ | √ | √ | |
不同包中的子类 | √ | √ | ||
不同包中的非子类 | √ |
3.代码示例
class BankUser{
private String userName; //私有成员属性,只能被改类中的方法访问修改
private int userAge;
private int userMoney;
private String userPhone;
int date; //注册日期,无修饰符
public String getUserName() { //public公开的方法,可以提供给方法调用者使用,get方法获取属性值
return userName;
}
public void setUserName(String userName) { //set方法设置属性值
this.userName = userName;
}
public int getUserAge() {
return userAge;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
public int getUserMoney() {
return userMoney;
}
public void setUserMoney(int userMoney) {
this.userMoney = userMoney;
}
public String getUserPhone() {
return userPhone;
}
public void setUserPhone(String userPhone) {
this.userPhone = userPhone;
}
@Override
public String toString() { //重写的toSting()方法,输出示例的各种属性
return "BankUser{" +
"userName='" + userName + '\'' +
", userAge='" + userAge + '\'' +
", userMoney=" + userMoney +
", userPhone='" + userPhone + '\'' +
'}';
}
}
public class Packaging {
public static void main(String[] args) {
BankUser bankUser = new BankUser(); //创建实例
//bankUser.userName = "王"
//成员属性被private修饰,其他类无法直接访问修改该属性,只能通过公开的方法修改
bankUser.setUserName("王");
bankUser.setUserAge(23);
bankUser.setUserMoney(20000);
bankUser.setUserPhone("131-9999-2131");
bankUser.date = 2020; //无修饰符的,可以在包内被直接访问修改
System.out.println(bankUser.toString());
}
}
二、继承
1.继承的意义
- 继承可以使代码更简洁
- 能提高代码的复用性
2、继承的实现
Java中的继承是单继承,使用extends
来实现继承
class Animal{
protected String name; //protected关键字修饰
public Animal(String name ) { //构造方法
this.name = name;
}
public void eat(){
System.out.println("吃东西");
}
}
class cat extends Animal{
public cat(String name) { //显式调用,super()调用父类构造方法
super(name);
}
@Override
public void eat() { //重写父类同名方法
System.out.println("cat::eat()");
}
}
class bird extends Animal{
public bird(String name) {
super(name);
}
@Override
public void eat() { //重写父类同名方法
System.out.println("bird::eat()");
}
public void fly(){ //自己的方法
System.out.println(this.name+"fly()");
}
}
public class ExtendsEx {
public static void main(String[] args) {
cat cat = new cat("咪咪");
cat.eat();
bird bird =new bird("小鸟");
bird.eat();
bird.fly();
Animal animal = new cat("咪咪"); //向上转型
animal.eat(); //结果输出cat::eat()
}
}
子类继承了父类除构造方法外的所有东西,子类只能用super()显式调用父类的构造方法,子类也继承了父类的成员属性(包括private修饰的成员属性),只是子类不能使用。
3、super
super:表示父类对象的引用,在子类中引用父类的成员属性和方法
- super.data :调用父类的成员属性
- super.func() :调用父类的方法
- super() :调用父类的构造方法
4、protected关键字
之前的表中已经介绍过protected关键字的范围,这里演示一下
package Code; //Code包
public class Animals {
protected String name; //protected修饰
public Animals(String name){
this.name = name;
}
public void eat() {
System.out.println("吃东西");
}
}
package extendsPack; //不同包
import Code.Animals;
/**
*@ClassName: frog
*@Description protected例子
*@Author PandaChan1
*@Date 2020/10/26
*@Time 21:25
*/
class frog extends Animals { //子类继承
public frog(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(this.name + " frog::eat()"); //访问修改
}
public void jump() {
System.out.println(this.name + " frog::jump()");
}
public void swim() {
System.out.println(this.name + " frog::swim()");
}
}
public class ProtectedTestDemo {
public static void main(String[] args) {
frog frog = new frog("呱呱");
frog.eat();
frog.jump();
frog.swim();
}
}
删除protected之后,另一个包会报错,即protected修饰的成员变量可以被其不同包的子类访问
5、final关键字
-
final修饰类时,表明改类不能被继承,如果一个类不想让其他类继承,就可以用final关键字修饰。final类中的所有成员方法都会被隐式指定为final方法
-
final修饰方法时,把方法锁定,以防任何继承类修改它。即父类的final方法是不能被子类覆盖(重写)的。
-
final修饰变量,final成员变量(属性)表示常量,只能被赋值一次,赋值后不能再更改,且该成员变量(属性)必须要显式初始化,两种初始化方法
class Person{ //private final int age = 0; //1、在声明时初始化 private final int age; public Person(){ this.age = 0; //2、声明时不初始化,但在构造函数中必须初始化 } }
-
final修饰基本数据类型的值时,表示该基本类型的值一旦在初始化后不可以再改变
-
final修饰引用类型时,则在对其初始化后便不能再指向其他对象了,但是引用所指向的对象的内容是可变的
class Person{ protected int a = 0; } public class Test{ public static void main(String[] args) { final Person person = new Person(); System.out.println(++person.a); //1 } }
6、向上转型
向上转型发生在子类和父类之间,指用父类接收子类的实例化对象(应该是这样理解,错误请指出)
看代码,更容易理解
public class ExtendsEx {
public static void main(String[] args) {
cat cat = new cat("咪咪");
cat.eat();
bird bird =new bird("小鸟");
bird.eat();
bird.fly();
Animal animal = new cat("咪咪"); //父类的引用指向子类的对象
animal.eat();
}
}
向上转型的三种方式:
-
直接赋值
Animal animal = new cat("咪咪");
-
方法的传参
public static void whatsAnimal(Animal animal) { //传参的过程中发生了向上转型 animal.eat(); } public static void main(String[] args) { Cat cat = new Cat("咪咪"); whatsAnimal(cat); }
-
方法的返回值
public static Animal whatAnimal(Cat cat) { //方法返回值中向上转型 return cat; } public static void main(String[] args) { Cat cat = new Cat("咪咪"); whatAnimal(cat).eat(); }
7、动态绑定
动态绑定是指在运行期间判断所引用对象的实际类型,并根据其实际类型来调用相应的方法。
前提:父类引用子类的对象,同时通过父类去调用父类和子类的同名覆盖方法(重写后的方法,在程序运行时,会去调用子类重写后的同名方法。
示例:
public class ExtendsEx {
public static void whatsAnimal(Animal animal) {
animal.eat();
}
public static void main(String[] args) {
Animal animal = new cat("咪咪"); //发生了向上转型
whatsAnimal(animal);
}
}
可以看到,我们在创建cat
对象时用cat
的父类Animal
接收该实例对象,在这个过程中发生了向上转型。在whatsAnimal()
方法中,参数为Animal
类型,调用eat()
方法。那么这里调用的是cat
中重写的eat()
方法还是父类Animal
的eat()
方法呢?
结果显示调用的方法为cat
中的方法,那么动态绑定在什么时候发生的呢?
反编译生成的class字节码文件得到如下信息,其中invokespecial
一般表示调用构造方法,invokevirtual
表示调用对象的方法,invokestatic
表示调用静态方法。可以看到在main
方法中我们调用了whatsAnimal()
这个静态方法,这个方法的参数类型为Animal
。然后在whatsAnimal()
方法中,看到箭头所指的位置,调用的是Animal
的eat()
方法。可是最后我们的得到的输出调用的是cat
重写的方法。可以得出结论:编译时不会发生动态绑定,运行时才发生动态绑定,且动态绑定发生的前要发生向上转型,并调用父类和子类的同名覆盖方法
三、多态
多态就是同一种行为,表现出不同的形式。即同一个接口,使用不同实例执行不同的操作。
1、多态的意义
多态可以简化代码,减少代码量;便扩展具有很好的灵活性和可扩充性。
2、多态的实现
2.1多态发生的条件
- 继承
- 重写
- 父类引用指向子类对象
就像之前写的:
public class ExtendsEx {
public static void whatsAnimal(Animal animal) {
animal.eat();
}
public static void main(String[] args) {
Animal animal = new cat("咪咪"); //发生了向上转型
whatsAnimal(animal);
}
}
main
方法中Animal
的引用指向子类的对象,然后在whatsAnimal()
中调用父类和子类的同名覆盖方法。
Cat
和Bird
中重写的父类方法只有eat()
方法,对不共有的方法,使用instanceOf
判断再执行相应的方法。
public class ExtendsEx {
public static void whatsAnimal(Animal animal) {
animal.eat();
if(animal instanceOf Bird) { //instanceOf判断一个对象是不是属于某个类
Bird bird = (Bird)animal;
bird.fly();
}b
}
public static void main(String[] args) {
Animal animal = new cat("咪咪"); //发生了向上转型
Animal animal1 = new Bird("小鸟");
whatsAnimal(animal);
whatsAnimal(animal1)
}
}
imal) {
animal.eat();
if(animal instanceOf Bird) { //instanceOf判断一个对象是不是属于某个类
Bird bird = (Bird)animal;
bird.fly();
}b
}
public static void main(String[] args) {
Animal animal = new cat(“咪咪”); //发生了向上转型
Animal animal1 = new Bird(“小鸟”);
whatsAnimal(animal);
whatsAnimal(animal1)
}
}