本篇文章主要讲解Java面向对象的知识点
- 面向对象的三大特性
- 类的扩展(抽象类,接口,内部类,枚举)
目录
面向对象和面向过程的区别?
面向对象的五大基本原则
面向对象三大特性
继承
怎么理解继承 ?
继承和聚合的区别?
封装
多态
什么是多态
什么是运行时多态 ?
多态的实现原理?
怎么理解动态绑定和静态绑定?
类的扩展
抽象类
抽象方法和抽象类
为什么需要抽象类 ?
final能修饰抽象类,抽象方法么?
抽象的方法是否可同时是静态的 是否可同时是本地方法 是否可同时被 synchronized 修饰?
抽象类必须要有抽象方法吗?
接口
接口代替继承 ?
Java支持多继承么,为什么?
抽象类和接口的区别 ?
内部类
什么是内部类?内部类的作用
内部类有几种,在项目中的有哪些应用
匿名内部类是什么?如何访问在其外面定义的变量呢?
Java内部类为什么可以访问到外部类呢?
静态内部类与非静态内部类有什么区别么 ?
枚举
Java 枚举类型是否可以继承 (final)?
Java 能否自定义一个类叫 java.lang.System?
面向对象和面向过程的区别?
两者的主要区别在于解决问题的方式不同:
解决问题方式不同 :
- 面向过程是以过程为中心的编程思想,是一种自顶向下的往下执行 把解决问题的过程拆成一个个步骤,每个步骤用函数实现,依次调用即可.(最典型的编程编程语言就是C语言)
- 优点 : 代码流程化,可以看出具体的每一步做什么,执行起来效率较高
- 缺点 : 代码重用性低,扩展能力差,后期维护难度比较大等问题.
- 面向对象先会抽象出对象,然后使用对象执行方法的方式来解决问题
- 优点 : 面向对象的编程方法,更符合人类的思维方式 , 面向对象开发的程序一般更易维护、易复用、易扩展.通过现实世界的理解和抽象运用封装,继承,多态等方法,通过抽象出对象的方式进行软件开发.
性能方面 :
面向过程的性能要比面向对象的性能要高,因为,面向对象先会抽象出对象,而创建对象需要实例化,比较消耗资源,所以当性能是最重要的因素,比如linux,单片机,嵌入式 一般采用面向过程开发
但这不是Java性能差的主要原因,因为面向过程也需要分配内存,计算偏移量,根本原因并不是因为它是面向对象语言,而是Java是半编译语言,最终的执行代码并不是直接被CPU执行的二进制机械码.面向过程语言大多都是直接编译成机械码在电脑上运行
Java语言是会先将源代码经过Javac进行编译成.class文件,也就是字节码,然后在通过 解释器&JIT来解释/编译生成平台所能理解的机器码,所以Java语言是解释与编译并存的.
安全性
面向对象比面向过程安全性更高, 面向对象将数据访问隐藏在类的内部中,而且类的成员变量和成员方法都有不同的访问权限. 而面向过程并没有合适的方法来隐藏程序数据。
面向对象的五大基本原则
单一职责原则
- 是指一个类的功能要单一,不能包罗万象。如同一个人一样,分配的工作不能太多,否则一天到晚虽然忙忙碌碌的,但效率却高不起来。
开放封闭原则
- 一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能,那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码,这要求在设计之初,就应当将服务端和客户端分开,公共部分抽象出来。
里氏替换原则
- 子类应当可以替换父类并出现在父类能够出现的任何地方。比如:公司搞年度晚会,所有员工可以参加抽奖,那么不管是老员工还是新员工,也不管是总部员工还是外派员工,都应当可以参加抽奖,否则这公司就不和谐了。
接口隔离原则
- 模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来
依赖倒置原则
- 具体依赖抽象,上层依赖下层。假设B是较A低的模块,但B需要使用到A的功能,这个时候,B不应当直接使用A中的具体类: 而应当由B定义一抽象接口,并由A来实现这个抽象接口,B只使用这个抽象接口:这样就达到了依赖倒置的目的,B也解除了对A的依赖,反过来是A依赖于B定义的抽象接口。通过上层模块难以避免依赖下层模块,假如B也直接依赖A的实现,那么就可能 造成循环依赖。一个常见的问题就是编译A模块时需要直接包含到B模块的cpp文件,而编译B时同样要直接包含到A的cpp文件。
面向对象三大特性
继承
怎么理解继承 ?
继承 : 按照现有类的类型来创建新类. 无需改变现有类的形式,采用现有类的形式并在其中添加新代码 即 :使用已存在的类作为基础来建立新的类,新的类可以增加新的数据和新的方法.
继承是所有OOP语言和Java语言不可缺少的组成部分.
当创建一个类的时候就是在继承,除非已经明确要从其它类中继承,否则就是隐式的从Object类进行继承. (Object类是所有类的父类).
继承需要使用一个关键字extends,当类需要继承的时候,就extends父类,这样就会自动得到父类中所有的域和方法.
- 子类继承了除构造方法的所有,即包括父类所有的属性和方法(也包含私有属性,方法),但子类无法访问父类的私有属性和方法,只是拥有
- 子类可以对父类进行扩展,(在原有类的基础上添加新的属性,方法...)
- 子类可以通过重写,重新实现父类的方法.
继承可以使代码重用性更好,但也会增加代码的复杂度和维护难度。
继承和聚合的区别?
继承和组合都是实现代码重用的机制.
组合 : 新的类是由现有类的对象组成. 只需要在新的类产生现有类的对象. 只是复用了现有程序代码的功能. 使用现有类开发新的类
//组合就是在新的类中添加现有类的对象
public class test {private String a,b,c,d;//现有类private List<Integer> res = new ArrayList<>();//先有类的对象private int i;private float j;public String toString() {......}}
继承 : 按照现有类的类型来创建新类. 无需改变现有类的形式,采用现有类的形式并在其中添加新代码 .即 :使用已存在的类作为基础来建立新的类,新的类可以增加新的数据和新的方法.
class Animal{private int id;private String name;private int age;public void eat(){.....}
}public class Dog{private int dogId;public void wangwang(){....}}
我们把继承看做是一种 is a 关系:比如狗是一个动物,猫是一个动物 即子类是父类的一种特殊情况
我们把组合看做是一种 has a的关系:比如学校有 学生,老师,工作人员组成。整体包含多个部分
我们都知道程序是先编译后运行,而继承关系就是在编译器编译的时候就确定好的。组合关系是在运行时确定的。
在Java中,继承是通过关键字extends实现的,聚合则是通过接口和构造函数实而现的。例如,一个汽车类可能继承自一个车辆类,而一个车轮类可能实现一个轮胎接口来描述它的属性和方法。
所以组合比继承更加简单灵活高效。我们如果非必要情况下优先选择组合。
当需要从新类向父类向上转型,则就需要继承.
封装
封装就是把一些内部的属性信息隐藏在对象的内部,不允许外部直接方位类的内部一些属性信息,但是可以提供一些公共的方法来操作这些内部属性和方法.
public class Student {private int id;//id属性私有化private String name;//name属性私有化//获取id的方法public int getId() {return id;}//设置id的方法public void setId(int id) {this.id = id;}//获取name的方法public String getName() {return name;}//设置name的方法public void setName(String name) {this.name = name;}
}
多态
什么是多态
多态就是指一个对象具有多种形态,就是同一操作作用域不同的对象,可以有不同的解释,产生不同的执行结果. 具体的表现就是 父类引用指向子类对象
Java中的多态是一种运行期的状态,为了实现运行期的多态即动态绑定,需要满足三个条件
- 有类继承和接口的实现(继承,接口实现)
- 子类要重写父类的方法(重写)
- 父类引用指向子类对象(向上转型)
多态的特点 :
- 对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
- 引用类型变量调用的到底是哪个类中的方法,必须在程序运行期间才能确定(Java中的多态是运行时多态)
- 多态不能调用 “只在子类存在但在父类不存在” 的方法;
- 如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法。
什么是运行时多态 ?
也就是运行期绑定,在程序运行期间,根据引用变量所执行的实际对象的类型,动态地选择相应的方法或操作,来调用.只有在运行期间才知道真正知道调用哪个类的哪个方法.
多态的实现原理?
怎么理解动态绑定和静态绑定?
静态绑定 : 也就是编译器绑定, 在编译期间根据方法签名(方法参数列表的参数顺序,参数个数,参数类型..)判断应该调用哪个方法
动态绑定 : 也就是运行期绑定,在程序运行期间,根据引用变量所执行的实际对象的类型来调用方法.只有在运行期间才知道真正知道调用哪个类的哪个方法.
类的扩展
抽象类
抽象方法和抽象类
抽象类和抽象方法都是用abstract关键字来声明
什么是抽象类
抽象类就抽象的类,抽象是相对于具体而言的,具体的类都有直接对应的对象,而抽象类没有. 如果一个类没有包含足够的信息来描绘一个具体的对象,这个类就是抽象类
比如 : 一个三角形是一个具体的对象,而图形就是抽象的概念. 樱桃是一个具体的对象,而水果就是抽象的概念.
抽象类与具体类的核心区别就是 : 抽象类不能创建对象,而具体类是可以的.
抽象类不能创建对象,要创建对象,必须使用它的具体子类(声明抽象类的变量,引用抽象类具体子类的对象). 一个类在继承抽象类后,必须实现抽象类中定义的所有方法,除非他自己也声明为抽象类
什么是抽象方法
方法不知道如何实现,只有子类知道是如何实现的. 这种方法就是抽象方法
抽象方法相对于具体而言的,具体方法实现代码,抽象方法只有声明,没有实现.
定义了抽象方法的类必须被声明为抽象类,抽象类可以没有抽象方法.
总结
- 如果一个类没有包含足够的信息来描绘一个具体的对象,这个类就是抽象类
- 被abstract修饰的方法称为抽象方法,被abstract修饰的类称为抽象类
- 定义了抽象方法的类必须被声明为抽象类,抽象类可以没有抽象方法.
- 抽象类不能实例化,除了不能实例化之外其他与普通类一样可以定义成员变量,成员方法,构造方法等。同时构造方法和类方法(被static修饰的方法)不能被abstract来修饰
- 抽象方法的访问修饰限定符不能是private,如果省略默认是public,同时抽象类和抽象方法不能被static和final修饰。
- 抽象类不能创建对象,要创建对象,必须使用它的具体子类(声明抽象类的变量,引用抽象类具体子类的对象). 一个类在继承抽象类后,必须实现抽象类中定义的所有方法,除非他自己也声明为抽象类
- 抽象类中的方法没有具体实现,要通过子类重写在子类中实现。
- 一个类只能继承一个抽象类
为什么需要抽象类 ?
引入抽象方法和抽象类是一种Java提供的语法工具. 对于一些类和方法,一道使用者正确使用他们,减少误用.
- 使用抽象方法而非空方法体,子类就知道它必须要实现该方法.而不能忽略,若忽略Java编译器会提示错误.
- 使用抽象类,类的使用者创建对象的时候,就知道必须要使用某个具体子类,而不可能误用不完整的父类.
final能修饰抽象类,抽象方法么?
- final不能修饰抽象类. 抽象类的出现就是被继承的(让其他类继承抽象类),从而让子类具体实现抽象类的方法.如果被final修饰,final修饰类表示该类不能被继承,没有子类. 这样就相互矛盾了
- final不能修饰抽象方法. 因为具体的方法实现由抽象类的子类重写具体实现.如果抽象方法被final修饰,表示该方法不能被重写.所以相互矛盾了
抽象的方法是否可同时是静态的 是否可同时是本地方法 是否可同时被 synchronized 修饰?
都不可以
- abstract与static
abstract:用来声明抽象方法,抽象方法没有方法体,不能被直接调用,必须由子类重写后才能调用.
static:用来声明静态方法,静态方法可以被类及其对象调用
static与abstract不能同时使用
用static声明方法表明这个方法在不生成类的实例时可直接被类调用,而abstract方法不能被调用,两者矛盾
- abstract与native
native:用来声明本地方法,该方法的实现由非java语言实现,比如C。一般用于java与外环境交互,或与操作系统交互
native可以与所有其它的java标识符连用,但是abstract除外。
本地方法是由本地代码(如 C 代码)实现的方法(有方法体实现),而抽象方法是没有实现的(无方法体),也是矛盾的
- abstract与synchronized
synchronized:修饰实例方法表示多个线程访问的时候只有一个线程能实现临界区的代码,synchronized修饰静态方法 ,可防止多个线程同时调用一个类的该方法
abstract与synchronized不能同时使用
从synchronized的功能也可以看出,用synchronized的前提是该方法可以被直接调用,显然和abstract不能连用,有矛盾.
抽象类必须要有抽象方法吗?
定义了抽象方法的类必须被声明为抽象类,抽象类可以没有抽象方法.
接口
接口声明/表示了一组能力,但它自己并没有实现这个能力,只是一个约定.是公共的行为规范的标准,在实现的时候,只要符合规范标准,既可以通用--->Java中,接口看做是,多个类的公共规范,是一种引用数据类型.
接口涉及交互两方对象, 一方需要实现这个接口,另一方使用这个接口,但双方对象并不直接互相依赖,只是通过接口间接交互.
总结接口
接口的定义与使用
- 接口声明/表示了一组能力,但它自己并没有实现这个能力,只是一个约定.是公共的行为规范的标准,在实现的时候,只要符合规范标准,就可以通用 .
- Java中,接口看做是,多个类的公共规范,是一种引用数据类型,但是不能直接new接口的对象
- 使用interface关键字来定义一个接口,默认为public
- 接口不能直接使用,必须由一个类来实现接口(类与接口之间使用implements关键字),实现接口中的所有抽象方法
- 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
- 接口中每一个方法都是 public 的抽象方法 , 即接口中的方法会被隐式的指定为 public abstract (只能是 public abstract, 其他修饰符都会报错).
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现
- 重写接口中方法时,不能使用 default 访问权限修饰符
- 接口中不能有静态代码块和构造方法
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量
- 在 Java 中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到 多继承的目的。 接口可以继承一个接口, 达到复用的效果 . 使用 extends 关键字 . 需要实现所有接口的方法
interface IRunning {
void run();
}
interface ISwimming {
void swim();
}
// 两栖的动物, 既能跑, 也能游
interface IAmphibious extends IRunning, ISwimming {
}
类与接口
- 一个类可以实现多个接口 , 一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。
继承与接口一起使用
类在继承的情况下,也可以实现多个接口.
public class Dog extends Animal implements IRunning,IEating{
...
}
接口的增强
-
在 jdk 7 或更早版本中,接⼝⾥⾯只能有常量变量和抽象⽅法。这些接⼝⽅法必须由选择实现接⼝的类实现。
-
jdk 8 的时候接⼝可以有默认⽅法和静态⽅法功能. 有具体的实现
-
jdk 9 在接⼝中引⼊了私有⽅法和私有静态⽅法。方便多个静态方法和默认方法能够复用代码
接口代替继承 ?
继承至少有两个好处:一个是复用代码;另一个是利用多态和动态绑定统一处理多种不同子类的对象。
使用组合替代继承,可以复用代码,但不能统一处理。
使用接口替代继承,针对接口编程,可以实现统一处理不同类型的对象,但接口没有代码实现,无法复用代码。
将组合和接口结合起来替代继承,就既可以统一处理,又可以复用代码了。
Java支持多继承么,为什么?
- 在 Java 中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到 多继承的目的。 接口可以继承一个接口, 达到复用的效果 . 使用 extends 关键字 . 需要实现所有接口的方法
interface IRunning {
void run();
}
interface ISwimming {
void swim();
}
// 两栖的动物, 既能跑, 也能游
interface IAmphibious extends IRunning, ISwimming {
}
抽象类和接口的区别 ?
抽象类和接口是配合而非替代的关系,他们经常一起使用,接口声明能力,抽象类提供默认实现,实现全部或者部分方法. 一个接口经常会有一个对应的抽象类
- Collection接口和对应的AbstractCollection类
- List接口和对应的AbstractList抽象类
- Map接口和对应的AbstractMap抽象类
抽象类和接口的共同点
- 都不能被实例化
- 都可以包含抽象方法
- 都有默认实现的方法,(对于接口而言Java8以后使用default关键字定义)
抽象类和接口的区别
- 如果一个类没有包含足够的信息来描绘一个具体的对象,这个类就是抽象类. 接口声明/表示了一组能力,但它自己并没有实现这个能力,只是一个约定.是公共的行为规范的标准,在实现的时候,只要符合规范标准,就可以通用 . 抽象是对类的抽象,是⼀种模板设计,⽽接⼝是对⾏为的抽象,是⼀种⾏为的规范。
- 组成 : 抽象类具有普通类所拥有的(构造方法,静态方法,实例变量,代码块....) + 抽象方法,但是抽象类不能实例化,接口由抽象方法(默认是public abstract修饰)和全局变量(默认是public static final修饰)组成
- 子类使用 : 抽象类-->extends关键字继承抽象类 , 接口--> 使用implements关键字实现接口
- 关系 : 一个抽象类可以实现若干个接口. 接口不能继承抽象类,但是接口可以使用extends关键字继承多个父接口-->表示具有多种能力
- 一个类只能继承一个抽象类,但是可以实现多个接口.
- 抽象类是单继承,而接口是多继承。
内部类
- 静态内部类
- 成员内部类
- 方法内部类
- 匿名内部类