Java中的三大特性 - 超详细篇 (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)进行访问和修改

为什么要封装呢?

追踪变化:可以在set方法中,编写代码来追踪属性的改变记录

public void setName(String name) { System.out.println("名字即将被修改"); System.out.println("旧名字:" + this.name); System.out.println("新名字:" + name); this.name = name; }

修改底层实现:在修改属性名时,不会影响外部接口对属性的访问

比如: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; }

校验数据:可以在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修饰符),会包含方法内容,这样别人可以直接调用接口类的方法(后面章节再细讲)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpgsds.html