前言

大家好啊,我是汤圆,明天给大家带来的是《Java中的三大个性 - 超具体篇》,心愿对大家有帮忙,谢谢

这一节的内容可能有点多,大家能够选择性的来看

简介

Java的三大个性:封装、继承、多态

乍一听,如同很高大上,其实当你真正用的时候,会发现高大上的还在前面呢。。。

热身

在正式解说三大个性之前,先遍及几个常识

1. 拜访权限修饰符

Java中对于拜访权限的四个修饰符,表格如下

privatefriendly(默认)protectedpublic
以后类拜访权限
包拜访权限×
子类拜访权限××
其余类拜访权限×××

其中比拟难堪的是protected修饰符,有点卡在两头,不上不下的感觉

因为它不适宜用来润饰属性

假如用它润饰属性,那么任何一个人都能够通过继承这个类,来间接拜访到这个类的属性,从而毁坏"封装性"

2. 抽象类(abstract)

什么是抽象类?

抽象类就是用abstract润饰,且不能被间接初始化的类,然而能够通过子类来初始化
比方:Father father = new Son()

对应的,形象办法就是用abstract润饰的办法

形象办法是一种很非凡的办法,它没有办法体,即办法实现代码为空,比方abstract public void fun();

形象办法个别在子类中进行实现,它就如同是在说:我不写代码,我只是申明一个办法名,剩下的交给我的子孙后代(继承类)去做

抽象类有一个很重要的特点:抽象类能够没有形象办法,然而如果一个类有形象办法,那么这个类必定是抽象类

为什么会有抽象类

解耦,使代码构造更加清晰

因为抽象类不能被间接创立为对象,它只是作为一个通用接口来供他人实现和调用,所以这样就使得形象的代码更加清晰(它只申明办法,不实现办法)
就好比,老板和员工,老板负责散发工作,员工负责去具体的实现工作

好了,对于抽象类,先介绍到这里,更具体的前面的章节再深刻

3. 重载(overloading)和覆写(overwriting)

重载和覆写是两个很容易混同的概念

重载:同一个类中,一个办法的多种表现形式(参数类型不同,参数个数不同)

覆写:继承设计中,子类笼罩父类的办法(也能够叫做重写,不过这样跟重载有点混同,所以集体喜爱叫做覆写)

他们之间的区别如下

重载覆写
拜访权限能够不同能够不同(然而子类的可见性不能比父类的低)
办法返回值能够不同雷同
参数类型不同(充分条件)雷同
参数个数不同(充分条件)雷同

这里要留神几点

  1. 覆写时,子类的办法拜访权限不能低于父类,比方父类办法为public,那么子类也只能为public
  2. 重载时,拜访权限和办法返回值,不能作为用来判断一个办法是否为重载的根据;只能说重载容许不同的拜访权限和返回值

覆写示范

代码示范如下,

// 覆写一:正确示范@Overridepublic void fun(){    System.out.println("son fun");}// 覆写二:谬误示范,拜访权限低了@Overrideprivate void fun(){    // 报错:'fun()' in 'SonDemo' clashes with 'fun()' in 'Father'; attempting to assign weaker access privileges ('private'); was 'public'    System.out.println("son fun");}
@Override这个是干嘛的?之前没见过啊

这个修饰符用来阐明这个办法是覆写办法,不写也能够,零碎会本人识别方法是不是覆写的

那为啥还要多此一举呢?用零碎默认的辨认机制不好吗?

要多此一举;不好;

因为加了注解,代码可读性更高,代码更加标准,他人看了代码后,立马就晓得这个办法是覆写办法

重载示范

重载用图展现可能会更加清晰

图示解释:

  1. 参数类型和参数个数,只有满足其一,就能够说这个办法被重载了
  2. 拜访权限和办法返回值用虚线框,是为了阐明他们两个只是重载的一个附加表现形式(可有可无),不能作为重载的判断根据

上面用代码演示下

// 根底办法public void fun1(int a){}// 重载一:参数个数不同public void fun1(){}// 重载二:参数类型不同public void fun1(float a){}// 重载三:谬误示范,仅仅用拜访权限的不同来重载private void fun1(int a){    // 编译报错:'fun1(int)' is already defined}// 重载四:谬误示范,仅仅用返回值的不同来重载public int fun1(int a){    // 编译报错:'fun1(int)' is already defined    return 0;}

上面进入注释,开始程序介绍这三大个性

注释

1. 封装(Encapsulation)

就是把类的属性私有化(private润饰),再通过私有办法(public)进行拜访和批改
为什么要封装呢?
  1. 追踪变动:能够在set办法中,编写代码来追踪属性的扭转记录

    public void setName(String name) {    System.out.println("名字行将被批改");    System.out.println("旧名字:" + this.name);    System.out.println("新名字:" + name);    this.name = name;}
  1. 批改底层实现:在批改属性名时,不会影响内部接口对属性的拜访

    比方:name属性改为firstName和lastName,name就能够在get办法中批改返回值为firstName+lastName,对外接口没变动
    // 批改前    private String name;      public String getName() {        return name;    }// 批改后    private String firstName;    private String lastName;    // 办法名不必变,只是办法内容作了批改    public String getName() {        return firstName + lastName;    }

  2. 校验数据:能够在set办法中,校验传来的数据是否合乎属性值的设定范畴,避免有效数据的乱入

    public void setAge(int age) throws Exception {    if(age>1000 || age<0){        throw new Exception("年龄不符合规范,0~1000");    }    this.age = age;}

2. 继承(Inheritance)

如果子类继承了父类,那么子类就能够复用父类的办法和属性,并且能够在此基础上新增办法和属性

这里要留神的一点是:Java是单继承语言,即每个类只能有一个父类

这里还要遍及一个常识:如果一个类没有指定父类(即没有继承任何类),那么这个类默认继承Object类

为什么要用继承呢?

为了代码复用,缩小反复工作

单继承不会太局限吗?为啥不必多继承?

因为多继承会导致"致命方块"问题(因为像扑克牌的方块符号)

  • 比方A同时继承B和C,而后B和C各自继承D
  • B和C各自覆写了D的fun办法
  • 那这时A该调用哪个类的fun办法呢

上面用图来谈话

那为什么叫致命方块,而不是致命三角形呢?那个D类如同是多余的

不多余

这个D类其实就是下面讲到的抽象类的作用:将共有的局部fun()形象进去(或者提供一个根底的实现),而后子类别离去实现各自的,这也是多态的一种体现(上面会将多态)

如果没有D类,那么B和C的fun()就会存在反复代码,这时你可能就想要搞一个父类进去了,这个父类就是D类

那要怎么判断继承类设计得好不好呢?

通过is-a关系来判断

is-a关系指的是一个是另一个的关系,男人是人(说得通),人是男人(一半说得通)

用is-a关系能够很好地体现你的继承类设计的好还是坏

  • 如果子类都能够说是一个父类,那么这个继承关系设计的就很好(男人是人,is-a关系)
  • 如果子类和父类只是蕴含或者援用的关系,那么这个继承关系就很蹩脚(猫是猫笼,蕴含关系)
有没有什么方法能够阻止类的继承?就像private修饰符用来封装属性,其他人拜访不到一样

有啊,final修饰符能够阻止类的继承

这里重点讲一下final修饰符

final能够用来润饰属性、办法、类,示意他们是常量,不可被批改的

final润饰属性:属性是常量,必须在定义时初始化,或者构造函数中初始化

final润饰办法:办法不能被覆写

final润饰类:类不能被继承

说到final,有必要提一下内联

内联指的是,如果一个办法内容很短,且没有被其余类覆写时,办法名会被间接替换为办法内容

比方:final getName()这个办法能够内联为name属性

再比方:getSum(){return a+b},会间接被内联为a+b

为什么会有内联这个货色呢?

因为这样能够提高效率(细节:CPU在解决办法调用的指令时,应用的分支转移会扰乱预取指令的策略,这个比拟底层,这里先简略介绍,前面章节再深刻)

那它有没有什么毛病呢?

有,如果一个办法内容过长,又误被当做内联解决,那么就会影响性能

比方你的代码多个中央都调用这个办法,那么你的代码就会收缩变得很大,从而影响性能

那有没有方法能够解决呢?

有,虚拟机的即时编译技术

即时编译会进行判断,如果一个办法内容很长,且被屡次调用,那么它会主动敞开内联机制,避免代码收缩

3. 多态(Polymorphism)

字面了解,就是多种状态,在Java中,多态指的是,一个类能够有多种体现状态

多态次要是 用来创立可扩大的程序

像咱们下面提到的继承就是属于多态的一种

还有一种就是接口(interface)

接口类一种是比抽象类更加形象的类

因为抽象类起码还能够实现办法,然而接口类没得选,就只能定义方法,不能实现

不过从Java8开始,接口反对定义默认办法和静态方法

接口的默认办法(default修饰符)和静态方法(static修饰符),会蕴含办法内容,这样他人能够间接调用接口类的办法(前面章节再细讲)

这样你会发现接口变得很像抽象类了,不过接口反对多实现(即一个类能够同时实现多个类,然而一个类同时只能继承一个类)

这样一来,Java相当于间接地实现了多继承

下图阐明继承和实现的区别:单继承,多实现

多态个别用在哪些场景呢?

场景很多,这里说两个最罕用的

  • 场景一:办法的参数,即办法定义时,父类作为办法的形参,而后调用时传入子类作为办法的实参
  • 场景二:办法的返回值,即办法定义时,父类作为办法的返回值,而后在办法外部理论返回子类

代码示范如下:

public class PolyphorismDemo {    public static void main(String[] args) {        PolyphorismDemo demo = new PolyphorismDemo();        //场景一:形参,将猫(子类)赋值给动物(父类)        demo.fun(new Cat());        //场景二:返回值,将猫赋值给动物        Animal animal = demo.fun2();    }        public void fun(Animal animal){            }        public Animal fun2(){        return new Cat();    }}class Animal{    }class Cat extends Animal{    }

总结

其中还有很多知识点没总结,太多了,看起来会不不便,所以其余的内容会陆续放到前面章节来讲

这里先简略列出来,比方:

  • equals和hashcode的关系
  • instanceof和getClass()的区别
  • 动态绑定和动静绑定
  • Java8的默认办法和静态方法
  • 等等等

后记

最初,感激大家的观看,谢谢