Java三大特性—封装、继承、多态
一、封装
学完之前的内容,我们一般不会在意属性的修饰符对吧。顶多写个public 修饰一下,但绝大多数都是默认的。但是不推荐大家这么做,为什么?我们来看下面
Dog dog = new Dog();
dog.health =-99999;
考虑到安全和权限的问题,属性不能让你随意赋值和更改。所以,引入封装。
封装的概念:
举个例子:就是生活中的快递包装盒,为了保护隐私安全。
具体步骤:
代码实现
public class Pet { //创建Pet类,下面全都改为private 修饰的属性
private String name;
private String petType;
private int healthValue;
private String gender;
private int intimacy;
public void introduce(){
System.out.println("我的名字叫"+this.getName()+",健康值为:"+this.getHealthValue()+",和主人的亲密度为"+this.getIntimacy()+",我的性别是"+this.getGender());
}
public String getName() { //这些就是getter方法
return name;
}
public void setName(String name) { //这些就是setter方法,快捷键【Alt+Insert】
this.name = name;
}
public String getPetType() {
return petType;
}
public void setPetType(String petType) {
this.petType = petType;
}
public int getHealthValue() {
return healthValue;
}
public void setHealthValue(int healthValue) { //定义规范
if(healthValue<0||healthValue>100){
System.out.println("输入不合法");
this.healthValue = 60;
}else {
this.healthValue = healthValue;
}
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
if(null==gender ||"公".equals(gender)){
this.gender = "公";
}else {
this.gender = gender;
}
}
public int getIntimacy() {
return intimacy;
}
public void setIntimacy(int intimacy) {
if(intimacy<0||intimacy>100){
System.out.println("输入不合法");
intimacy = 20;
}
this.intimacy = intimacy;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Pet pet = new Pet();
System.out.println("请输入宠物的名字:");
String name =sc.next();
pet.setName(name);
System.out.println("请选择宠物的类型:(1.狗狗 2.企鹅)"); //写在setter外面的
int type =sc.nextInt();
if(type==1){
pet.setPetType("狗狗");
}else {
pet.setPetType("企鹅");
}
System.out.println("请选择"+pet.petType+"的性别:1.Q仔2.Q妹");
int genderChoice = sc.nextInt();
if(genderChoice==1){
pet.setGender("Q仔");
}else {
pet.setGender("Q妹");
}
System.out.println("请输入"+pet.getPetType()+"的健康值"); //写在setter里面的
pet.setHealthValue(sc.nextInt());
System.out.println("请输入"+pet.getPetType()+"的亲密度");
pet.setIntimacy(sc.nextInt());
pet.introduce();
}
}
代码虽然看上去有点多,其实很简单,就是几个属性,加一堆getter,setter方法。
然后就是包,注意package 包名; 全部小写,要写在代码的第一行
为什么要引入包这个概念,注意看前一章,修饰符的权限。
当然要引入包的话,要用关键字import 包名.类名或者import 包名.*,这个写在package下面
这个之前写Scanner的时候也有注意到吗
二、继承
在Java中,我们使用extends关键字来表示继承关系
形如:
class A{
属性
方法…
}
class B extends A{
属性
方法…
}
我们称A为B的父类,B为A的子类。
那我们为什么要使用继承呢
把一些公共的代码提取出来,形成父类,特有的作为子类,这样方便代码的修改和重复代码的创建。
这里注意一下一个类只能继承一个父类,但是一个父类可以有多个子类,这个很好理解吧。
这里要注意两点:
1:super和之前讲的this差不多一个是指父类,一个是指本类(当前对象)。
2:继承就是子类拥有父类的所有东西,至于被private修饰的属性不能被访问,其实只是隐藏了,后面讲反射可以进行访问,还有就是static(静态变量:类变量),它是属于这个类本身的,不属于单个对象,所以也不可以被访问。
继承条件下的构造方法
这里可以看一看前面转载的一片构造器,静态方法,执行顺序那篇文章。
最后我们通过一个例子来讲一下继承
/**
* @Author shall潇
* @Date 2021/1/19
* @Description
*/
//Car类
class Car {
private int site = 4; //座位数
Car(){
System.out.println ("载客量是"+site+"人");
}
public void setSite(int site){
this.site = site;
}
void print(){
System.out.print("载客量是"+site+"人");
}
public static void main(String[] args) {
Bus bus = new Bus(20);
bus.print();
}
}
//Bus类继承Car类
class Bus extends Car{
Bus(int site){
setSite(site);
}
}
结果显示
第一个4人是先调用了父类的构造器,但是并没有用到参数20,而是使用了父类本身的默认值
第二个20人是调用了父类的print的方法,这个时候用到参数20
三、多态
多态核心其实就是重载(override)和重写(overload)
多态的四种表现形式:重载,重写,抽象类,接口
1.重载
在一个类中(其实这个不是必要的,当没有子类时就必须要这个条件),相同的方法名,参数列表不同。和其他东西无关。它是根据传入的参数类型,个数自动进行匹配不同的方法。
【特殊的:在子类中,也可以实现父类的方法重载】
【我们来举个特殊的例子,正规的我就不举例子了,大家可以自己试一下】
父类:Pet,父类中方法:getPet方法
子类:Cat
注意看子类中这两个方法前面的标志,第一个没有重写标志,但是没有报错,说明它就是重载,第二个则有。
和父类方法名相同,参数列表不同,但是尽管方法在不同的类中,它也是重载。
2.重写
要在不同的类中,一般在子类中重新改写父类方法。方法名不变,参数列表没变,只是方法内部改变了。
【重载规则】
方法名相同
参数列表相同
返回值类型相同或者是其子类
访问权限不能严于父类
父类的静态方法不能被子类覆盖为非静态方法,父类的非静态方法不能被子类覆盖为静态方法
子类可以定义与父类同名的静态方法,以便在子类中隐藏父类的静态方法(注:静态方法中无法使用super)
父类的私有方法不能被子类覆盖
不能抛出比父类方法更多的异常
【举个例子】
父类,父类方法:show
public class Pet {
private String name;
private int health;
private int love;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public int getLove() {
return love;
}
public void setLove(int love) {
this.love = love;
}
public String show(){ //这里要被重写
return "我叫:"+this.name+"我的健康值:"+this.health+"爱心值为:"+this.love;
}
public Pet getPet(){
return new Pet();//返回一个Pet对象
}
}
子类,子类方法:show
public class Cat extends Pet{
private String strain; //多了一个 品种属性
public String getStrain() {
return strain;
}
public void setStrain(String strain) {
this.strain = strain;
}
public String show(){ //方法重写
String s = super.show(); //先调用父类 的show方法
return s+"品种为"+this.strain;//再加入自己的属性
}
public Pet getPet(String name){
return null;
}
public Pet getPet(){
return new Pet();
}
}
测试类
public static void main(String[] args) {
Cat cat = new Cat();
cat.setName("葡萄");
cat.setHealth(76);
cat.setLove(67);
cat.setStrain("暹罗猫");
System.out.println(cat.show());
结果显示
既然我们可以重写自己定义的方法,那么可不可以重写Java的方法?
当然是可以的,例如:object类(它是所有类的父类)里面的toString、equals、getClass、hashcode等方法。
接下来,我们就拿重写equals方法来举例,定义自己的比较标准。
【这里我们定义两个猫如果品种相同就是相同】
子类:Cat
测试类
Cat cat = new Cat();
cat.setName("葡萄");
cat.setHealth(76);
cat.setLove(67);
cat.setStrain("暹罗猫");
Cat cat1 = new Cat();
cat1.setName("豆豆");
cat1.setHealth(26);
cat1.setLove(37);
cat1.setStrain("暹罗猫");
System.out.println(cat.equals(cat1));
输出结果
重写完之后,我们正式进入多态
开始之前,我先抛个砖:大盒子能装小东西,小盒子能装大东西吗?用大盒子装舒服还是小盒子舒服?
现在我们要定义一个主人类:Master,主人类中只有带宠物看病这个方法。注意,我们这里写的是宠物,而不是确定的。
Master类
public class Master {
public void cure(Pet p){ //注意看参数类型
p.toHospital(); //之前的Pet类中没有这个方法,我们添加一下
}
}
Pet类
public void toHospital(){
System.out.println("父类去医院");//当然,子类在重写后,会覆盖掉
}
Cat类
@Override //重写父类的看病方法
public void toHospital() {
if(this.getHealth()<50){
System.out.println("猫咪生病了,建议给猫咪看病");
setHealth(60);
}else {
System.out.println("您的猫咪很健康");
}
}
测试类
Cat cat1 = new Cat();
cat1.setName("豆豆");
cat1.setHealth(26);
cat1.setLove(37);
cat1.setStrain("暹罗猫");
Master master = new Master();
master.cure(cat1);
结果显示
看,对于Master来说,不管你是什么猫啊,狗啊,只要继承Pet类,都可以往我这进,只要进来我就带你去看病。
那多态就是这些吗?当然不是
多态:子类对象引用父类类型 Pet p = new Cat();
形如这样的才是真正地运用了多态特性。为什么要这样写呢?我们接着往下看:
其实上面这句话意思是:创建Pet类型的Cat对象,可能大家又要懵了,其实就是上面讲到的拿大箱子装小东西,我们来举个例子
Cat类
public void catchMouse(){
System.out.println("抓老鼠"); //加了一个子类独有的方法
}
测试类
Pet p2 = new Cat();
p2.setName("fofo");
p2.setHealth(78);
p2.setLove(56);
((Cat) p2).setStrain("加拿大无毛猫"); //调用子类特有的属性
((Cat) p2).catchMouse(); //调用子类特有的方法
输出就是抓老鼠。
我们可以注意到。当我们创建Pet类型的Cat对象时,想要调用Cat类里面独有的方法时,需要强制类型转换。可能大家有疑惑,为什么不直接创建Cat类型的对象,当然是为了思考更直接。我只需要Pet类型,不需要什么猫啊,狗啊,全部都是Pet类型,等到我需要你的特有的方法时,再转换一下类型。
当然如果不是继承关系怎么办?
那么我们通过instanceof关键字来判断是否为继承关系
例如:
if(p2 instanceof Cat)
((Cat) p2).catchMouse();