封装
封装就是把事物的属性和方法放到一个类里面去,并用private修饰,不允许在外部直接访问,只能通过类提供的公开方法进行间接访问
Cat类封装方法和属性后,在外部不能直接访问,主要原因是用到了private修饰。
那我们要怎样去访问类的方法和属性呢?
提供相应的get,set方法。
这里用this.age,this.name来给本类的属性赋值,this就是指本类。
大家可能会想,我直接用public修饰,让外部可以直接访问不好吗,非要再用set、get方法去访问不是多此一举吗?其实不然,因为外部的人可以不小心会赋错值。
比如你给猫咪的年龄是1000岁,你觉得合理吗,所以我们可以在set,get方法中增加一些业务逻辑,这里我们可以来限制一下猫咪的年龄。
构造器语法: 修饰符 类名(参数列表){。。。}
列如:public Cat(){…}
当然,这里还要说一下的是构造器(构造方法),就是一个方法。创建对象时,系统会自动调用构造器完成对类的初始化。系统默认会生成一个无参构造器,你也可以自己写有参构造器。在创建对象的同时进行初始化(给属性赋值)
这里就可能通过构造器赋错值。所以我们可以把set方法写进构造器,这样就可以防止。
总结一下封装的好处:
1、把方法和属性都封装起来,外部的程序员只需要学会调用方法就行,不需要知道实现细节。减少了学习类的成本,使用更便利
2、不允许外部程序员直接调用属性和方法,确保了数据的安全合理性。还可以进行安全验证
继承
首先我们来看一段代码
我们可以看到Cat和Dog类代码基本一样。这就显得代码冗余。继承机制了就改善这个问题
我们可以把相同的那一部分代码提炼出来当做父类,让其它需要这些的类继承此类,成为子类。子类继承了父类,子类就拥有了父类的属性和方法。
继承细节
1、子类虽然继承了父类的属性和方法,但父类私有的属性和方法不能再子类直接访问(private修饰的只能在本类访问,前面有讲),要通过父类提供公共的方法去间接访问。
2、Java是单继承机制,即子类只能有一个直接父类,但父类又可以有其父类(这里就叫爷爷类)。。。顶级父类为object类,object类是所有类的(间接)父类
A继承B,B再继承C是可以的,B为A的直接父类,C为A的间接父类,A也拥有C的属性和方法;但A继承B,C是不允许的。
3、创建子类对象时(子类运行时),会先调用其爷爷类的(无参)构造器,完成爷爷类的初始化,再调用父类的(无参)构造器,最后调用自己的构造器
4、不管使用子类的那个构造器,默认都会先调用父类的无参构造器。但若父类没有无参构造器或有其他有参构造器,则必须再子类构造器中用super显示的说明调用父类的那个构造器,即完成自己的初始化之前一定要先完成父类的初始化,否则编译不过。
Cat类没有完成animal类的初始化(有参构造器)
animal类为有参构造器,则Cat类需显示的super调用父类的构造器;Cat类为无参构造器,则调用LittleCat类的构造器完成初始化之前,系统默认自动调用Cat类的无参构造器(在底层,系统默认在无参构造器内加上了super()😉,无需显示调用。
补充:
1、super指的是父类,super只能在类定义的方法中使用。只能访问父类公开的属性和方法,不能访问私有的。
2、this指本类,this不能再类定义的外部使用,只能在类定义的方法中使用。
多态
多态:简单来说就是方法和对象具有多种形态,具体分为方法的多态和对象的多态
多态是建立在封装和继承之上的,有封装继承,才有多态。
这里先介绍多态的向上转型和向下转型
1、向上转型
父类的引用指向了子类的对象
语法: 父类类型 引用名(对象名) = new 子类类型();
2、向下转型
语法 : 子类类型 引用名 = (子类类型)父类引用
巧理解:new了一只猫,把它向上转型为一只动物类Animal animal1=new Cat();此时它是一只动物,但它的本质是一只猫,所以又可以通过向下转型又转成猫Cat cat = (Cat)animal1;。
可以看到dog向上转型转成其父类animal后,不能再调用Cat的方法和属性;
向下转型转回成dog后,才能重新调用dog类的方法和属性。
多态的作用:依旧可以让代码减少冗余,简化代码,提高代码的可扩展性
如果还有很多动物都要写吃饭这个方法,就很繁琐;运用多态的向上转型就可以减少代码。
以后新增其他动物,也不用也eat方法,直接调用其父类的eat方法,把子类对象传入即可。扩展性极强
方法和对象的多态
1、方法的多态:
重载:方法名必须相同,参数列表必须不同
重写:方法名,参数列表,返回值都相同。
2、对象的多态(向上,向下转型)
Animal animal1=new Cat();
一个对象的编译类型和运行类型可以不一致
编译类型看等号的左边(Animal),运行类型看等号右边(Cat)
代码在运行时看运行类型,代码编译时看编译类型。
编译时,animal1的编译类型为Animal类,没有eat方法,所以编译过不去。
重点:java动态绑定机制
1、当调用对象方法时,该方法会和该对象的运行类型绑定
2、当调用对象属性时,没有绑定,哪里声明,哪里调用。
调用方法,与运行类型绑定。
调用属性,没有绑定机制。