1. 封装
封装的最基本单位是对象,封装的最基本目标是“高内聚、低耦合”。通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象,我们在类中编写的方法就是对实现细节的一种封装,我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。
2. 继承
- Java是单继承
- 继承好处:提高了代码的复用性、维护性
- 继承弊端:类的耦合性增强了
- 继承体现的关系:is a
- 设计时可以抽象子类的共有特征来构成父类
- this:代表本类当前对象的引用
- super:代表父类对象的引用,super会先去父类找,父类没有就去爷爷类找,Object类也没有就编译报错
- 对构造的调用:只能出现在其他构造的第一句;因此this()和super()不能同时出现在同一个构造中;在成员方法中不能用this()或super()调用构造
- 继承中变量的访问顺序:
- 子类局部范围找
- 子类成员范围找
- 父类成员范围找
- 如果都没有就报错(不考虑父类的父类)
- 继承中成员方法的访问顺序:
- 子类成员范围找
- 父类成员范围找
- 如果都没有就报错(不考虑父类的父类)
- 继承中构造方法的访问特点:
- 子类中所有构造方法第一条语句默认都是:super(),因为子类会继承父类中的数据,可能还会使用父类中的数据,因此子类初始化之前,一定要先完成父类数据的初始化。
- 若父类中没有无参构造,只有带参构造,需要显示使用super去调用父类带参构造方法,因此创建类时最好都写上无参构造。
- 构造代码块:直接在类中定义且没有加static关键字的代码块;构造代码块在每次创建对象时被调用,且优先于构造方法执行
- 可以统计创建了多少对象
- 可以放创建对象之前需要执行的代码
- 方法重写时子类方法访问权限不能比父类的低
- 一个类如果没有父类,默认继承自Object类
- 运行时,父类先进方法区;父类中的属性,会跟随子类对象进入到堆内存
3. 多态
- 多态简单理解:同一个对象在不同时刻表现出不同形态
- 多态的前提和体现:
- 有继承/实现关系
- 有方法重写
- 有父(类/接口)引用指向(子/实现)类对象
- 多态好处:提高了程序的扩展性;如在定义方法时,使用父类作为参数,将来使用时用具体子类参与操作
- 多态弊端:不能使用子类特有功能
- 多态中成员的访问特点:只有运行时的非静态方法看右边
- 编译时:变量和方法都看左边。因为编译时右边new的对象还没有被调入到堆内存。
- 运行时:变量看左边,静态方法看左边,非静态方法看右边。因为左边的声明类型可能是接口,方法是抽象的,所以运行时方法要看右边的。
- 向上转型:子类转换为父类,父类引用指向子类对象,安全
- 向下转型:父类转换为子类,父类引用转为子类引用,不安全
- instanceof运算符:判断一个对象是否为一个类的实例
4. 抽象类
- 抽象类主要对类进行抽象
- 抽象类体现了模板方法模式
- 使用abstract修饰抽象类和抽象方法
- 一个没有方法体的方法应该定义为抽象方法
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 抽象类的子类要么重写抽象类中的所有抽象方法,要么是抽象类
- 抽象类不能实例化,但有构造方法,用于子类访问父类数据的初始化
- abstract和final不能同时使用,因为abstract是让子类重写,而final是不可重写
- abstract和private不能同时使用,因为abstract是让子类重写,private要求只能本类使用,子类根本继承不了
- abstract和static不能同时使用,因为abstract是让子类重写,static修饰的方法不能被重写
5. 接口
-
接口主要对行为进行抽象
-
一个类/接口可以同时实现/继承多个接口
-
接口不能直接实例化,可以通过实现类对象实例化
-
接口的实现类要么重写接口中的所有抽象方法,要么是抽象类
-
接口没有构造方法
-
接口中的成员变量只能是常量,默认修饰符为public static final
-
接口中的方法只能是抽象方法,默认修饰符为public abstract
-
接口多实现冲突(冲突即重名):
- 接口常量冲突:该常量不会被继承,只能通过原接口访问
- 接口抽象方法冲突:只需重写一个
- 接口默认方法冲突:实现类必须重写这个默认方法;因为是类,所以不需要加default
- 接口静态方法冲突:静态只供接口调用,对实现类无影响
-
接口多继承冲突:
- 接口常量冲突:该常量不会被继承,只能通过原接口访问
- 接口抽象方法冲突:只会继承一个
- 接口默认方法冲突:子接口必须重写这个默认方法;因为是接口,所以需要加default
- 接口静态方法冲突:静态只供接口调用,对子接口无影响
-
实现类继承父类又实现接口冲突:
- 类和接口常量冲突:该常量不会被继承,只能通过原接口或父类名访问
- 类和接口抽象方法冲突:只会继承一个
- 类和接口默认方法冲突:实现类优先使用父类的
- 类和接口静态方法冲突:实现类只能访问父类的静态方法
-
接口的组成:
- 常量
- 抽象方法
- 默认方法(Java 8)
- 静态方法(Java 8)
- 私有方法(Java 9)
-
接口中默认方法不是抽象方法,所以不强制被重写,但可以被重写,重写的时候去掉default关键字
接口中默认方法的定义格式: public default 返回值类型 方法名(参数列表) {} 定义时public可以省略,default不能省略
-
接口中静态方法只能通过接口名调用,不能通过实现类名或者对象名调用,因为接口的静态方法不能被继承
接口中静态方法的定义格式: public static 返回值类型 方法名(参数列表) {} 定义时public可以省略,static不能省略
-
接口中私有方法:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性
接口中私有方法的定义格式: 格式1:private 返回值类型 方法名(参数列表) {} 格式2:private static 返回值类型 方法名(参数列表) {} 默认方法可以调用私有的静态方法和非静态方法 静态方法只能调用私有的静态方法
6. 内部类
-
内部类的访问特点:
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
-
外界创建非私有的成员内部类:
外部类名.内部类名 对象名 = 外部类对象.内部类对象; 如:Outer.Inner oi = new Outer().new Inner();
-
局部内部类是在方法中定义的类,外界无法直接使用,需要在方法内部创建对象并使用。
-
局部内部类只能访问final的局部变量,在JDK1.8之后,若没有加final则会默认加上。因为局部变量随方法结束而销毁时,局部内部类对象可能还没有被回收,因此被内部类访问的局部变量会被拷贝一份到内部类中,若局部变量不是final的,其值就可能被修改,而内部类对象中保存的是其原来的值,这就会出现数据不同步的问题。
-
匿名内部类:本质是一个继承了该类或者实现了该接口的子类匿名对象
格式: new 类名或接口名() { 重写方法; }
7. 成员变量和局部变量的区别
成员变量 | 局部变量 |
---|---|
在类中,方法外 | 在方法内或方法声明上 |
堆内存 | 栈内存 |
随对象的存在而存在 | 随方法的调用而存在 |
有默认值 | 无默认值 |
8. 访问权限
权限 | 同类 | 同包 | 不同包子类 | 不同包非子类 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | |
default(不写) | √ | √ | ||
private | √ |
外部类只能用public或default(不写)这两种修饰符;内部类四种都可以用
protected权限用法:只能在子类中使用super才能访问父类
private修饰的方法不能被重写,因为private修饰的成员只能在本类中访问
9. static
-
被static修饰的内容在静态区中,随着类的加载而加载;不属于对象,而是属于类的;凡是本类的对象,都共享同一份。
class Student { private int id; private static int idCounter = 0; // idCounter:学号计数器 public Student() { // 每当new一个新对象时,自动给id赋值 id = ++idCounter; } }
-
静态代码块:
-
特点:当第一次用到本类时,静态代码块执行唯一的一次**(无论创建多少对象,只在第一次的时候执行)**
-
典型用途:用来一次性地对静态成员变量进行赋值
-
静态代码块优先于构造代码块;构造代码块优先于构造方法
static { // 静态代码块的内容 }
-
-
通过类名访问静态成员变量时,全程和对象就没关系,只和类有关系
-
无论成员变量还是成员方法,如果被static修饰了,都推荐使用类名进行调用
-
静态不能直接访问非静态,因为在内存中先有静态,后有非静态。–>前人不知道后人,但后人知道前人
-
static不可以修饰局部变量,因为静态成员属于类,不属于方法
-
静态方法不能使用this,因为this代表当前对象,通过谁调用的方法,谁就是对象,但是静态却与对象没关系
-
静态方法可以被继承,但是不能重写。如果父类中有一个静态的方法,子类也有一个与其方法名参数列表相同静态方法,那么该子类的方法会把原来继承过来的父类的方法隐藏,而不是重写。
-
静态内部类可以定义静态和非静态的方法和属性,而非静态内部类只能定义非静态方法和属性。因为非静态内部类在创建对象时才出现,若内部有静态成员,则该静态成员需在创建对象前就出现,相互矛盾。
-
静态属于类,不属于对象,因此静态不能对象序列化
10. final
-
被final修饰的类不能被继承;一个类如果是final的,那么其中所有成员方法都无法被覆盖重写(因为没儿子)
// 格式 权限修饰符 final class 类名称 { }
-
被final修饰的方法不能被覆盖重写
// 格式 权限修饰符 final 返回值类型 方法名(参数列表) { }
-
被final修饰的局部变量不可改变,一次赋值,终生不变。对于基本类型来说,不可变指变量中的数据不可改变;对于引用类型来说,不可变指变量的地址不可改变
// 格式 只保证有唯一一次赋值即可 final 数据类型 变量名;
-
被final修饰的成员变量同样不可变且没有默认值
- 被final修饰的成员变量,要么直接赋值,要么通过构造方法赋值,二者选其一。
- 如果选择使用构造方法赋值,那么必须保证类中所有重载的构造方法,都对被final修饰的成员变量赋值。
- 被final修饰的成员变量,不能有set方法。
11. 枚举
-
枚举是JDK1.5新增的引用数据类型,枚举和类、接口、注解是一个级别,定义枚举的关键字为enum
-
所有枚举默认继承了java.lang.Enum,因此枚举不能继承其他类,但枚举可以实现接口
-
每一个枚举及其定义的常量在JVM中都是唯一的,为了达到这个效果,它通过以下方法来确保:
- 私有构造,无法正常的new对象
- 无法通过反射的方式创建枚举对象
- 无法通过clone()方法克隆枚举对象
- 类加载时创建,保证线程安全
- 对序列化进行特殊处理,防止反序列化时创建新的对象
-
枚举中每一个常量都可以看作是该枚举的实例,默认被public static final修饰
-
枚举中的常量一般大写,多个常量之间用逗号分开,最后一个常量后可以写分号也可以不写
-
枚举构造默认是private且只能是private;而且构造方法必须写在常量后面,这时最后一个常量后必须写分号
/** * 枚举定义和使用示例 */ enum Color { // 枚举静态常量 RED, GREEN, BLUE } enum WebPage { /** * 自己写了有参构造,因此不会再提供无参构造 * 而枚举中每一个常量都可以看作是该枚举的实例,因此定义时需要传递参数 */ HTML("HTML语言"), CSS("CSS语言"), JAVASCRIPT("JavaScript语言"); private String describe; // 枚举构造默认是private且只能是private WebPage(String describe) { this.describe = describe; } public String getDescribe() { return describe; } } public class EnumTest { public static void main(String[] args) { Color red = Color.RED; System.out.println(red); System.out.println(WebPage.JAVASCRIPT.getDescribe()); /** * 结果: * RED * JavaScript语言 */ } }
若有错误或补充,欢迎私信