目录
拓1:私有方法的优点
- 代码重用:减少重复代码。
- 封装性:隐藏不需要暴露给使用者的实现细节。
拓2:静态方法的优点
- 工具性:提供与 类/接口 相关的工具方法。
- 独立性:不依赖于任何特定实例。
拓3:类的五大成分
拓4:硬编码和软编码
- 硬编码:将具体的数值、路径、参数等直接写入程序代码中
- 软编码:将具体的数值、路径、参数等用 变量、配置文件等方式保存,将数据与源代码解耦,方便我们后续查找和修改数据,提高了代码的维护性。缺点是增加了CPU负担,降低了性能。
一、static
1. 概念
2. 成员变量在内存中执行原理
3. 类变量(静态变量)的使用场景
数据共享、对象计数器
4. 两种成员变量
5. 两种成员方法
6. 类方法的使用场景
做工具类
static修饰方法(类方法):
好处是可以通过类直接调用,无需创建实例,还可以实现静态导入。
虽然类方法不需要创建对象调用,更节约内存,但不要把所有方法都写成静态方法,因为这不符合面向对象的封装设计原则、会导致线程安全问题。
7. 搞懂main方法
a. main方法是类方法
b. args参数
8. static成员的访问和5限制
9. 代码块
a. 静态代码块
在代码加载到方法区时就会执行。
作用是完成类变量的复杂初始化。
b. 实例代码块
在每次创建对象时执行。
作用是完成对象初始化,减少重复代码:所以构造器中都有的代码可以直接写在实例代码块中,从而减少重复。
二、单例设计模式
1. 是什么设计模式?
2. 单例设计模式
a. 是什么?
确保类只有一个对象。
b. 应用场景和好处
使用单例模式可以避免浪费内存。
c. 单例模式的实现方式
- 饿汉模式:
使用对象前就已经把对象创建好了。
懒汉模式:拿对象时才开始创建对象。
什么时候用饿汉什么时候用懒汉?
如果是频繁使用的对象,就用饿汉。反之用懒汉,因为这样节约内存。
三、继承
1. 概念
a. 是什么?
使用extends关键字,让一个类和另一个类建立起父子关系,让子类拥有父类的非私有成员。
b. 继承的特点
- 子类只能继承父类的非私有成员。
- 子类对象是通过子类和父类两张蓝图来创建的。
- 子类对象在类外只能访问子类和父类的非私有成员。
c. 继承的执行原理
创建带有继承的对象时,是用父类和子类两张蓝图一起来创建的。
对象在堆内存中,会单独存在一块super区域,用来存放父类的数据:
d. 为什么需要继承?
- 减少重复代码的编写,提高了代码的复用性:
把多个类中的重复代码放到一个类中,让这个类成为父类,其余类继承该类。- 实现多态。
2. 权限修饰符
a. 是什么?
就是写在类名或成员名前,用来限制类或成员访问范围的关键字。
b. 默认权限
- 如果类名或成员名前不写权限修饰符,则权限为默认(default、缺省),只能同包访问。
- 没有关键字来声明默认权限,如果一个类、成员前不使用任何访问修饰符(即不使用
public
、protected
或private
),则它的访问权限就是默认的(default)。
c. protected:在任意子类中
注意是在任意包下的子类中,而不是子类对象。
不同包下的子类中,只能访问protected成员和public成员:
protected是指在任意包下的子类中,而不是子类对象:
3. Java是单继承的
java是单继承的,java中的类不支持多继承,及一个子类继承多个父类,但支持多层继承。
这样做的好处是规避了,如c++中菱形继承(钻石继承)等各种麻烦的问题。
4. Object类
a. 是什么?
Object是所有类的祖宗类,不管有没有写extends Object,所有类都还是直接或间接的继承了Object类。
如果Z类已经显示写继承了其他类,那它就间接继承于Object类,是Object子孙类。因为它往上某代(假设为A类)一定没有显示写继承了其他类,那么A类就一定继承了Object类,于是A类往下包括Z类就一定间接继承了Object类。
b. 为什么?
即便我们不写,但类中还是会有很多写好的方法,这些方法就是继承于Object类的:
5. 重写(Override)
a. 是什么?
声明不变,重新实现。
b. 注意事项
c. 应用场景
常见的应用场景有:重写tostring方法。
6. this 和 super
在子类中访问和父类同名的成员变量时遵循就近原则,也可以使用this和super指定访问。
7. 子类构造器的特点
a. 要先调用父类构造器
所有子类构造器的第一行(无论有参无参)如果不显示调用父类构造器,就会在编译时默认添加一句调用父类无参构造器的代码。所以建议保证类中有一个无参构造器。
可以使用super()显示调用父类构造器。
b. 为什么?
因为继承的关系对象的属性分布在多个类中,所以在子类构造器中强制先调用父类构造器,这样可以保证完成对继承下来的父类成员变量的初始化,再使用子类构造器其他语句完成对子类成员变量的初始化。这样才能保证能完整处理对象的属性。
c. 使用this调用兄弟构造器
在构造器的第一行我们可以使用this去调用本类的其他构造器,从而实现代码的复用。
d. 构造器中不能同时有this和super
因为this调用的其他构造器中也含有super(),这样子类构造器中就调用了两次父类构造器。而且this()和super()都要求在构造器中的第一行。
四、多态
1. 概念
a. 是什么?
多态是对象和行为的多态,对属性(成员变量)不谈多态。
b. 对象多态
对象多态:指一个父类引用可以指向不同的子类对象。(对应现实生活中,一个男人可以是爸爸、老师、丈夫。)
c. 行为多态
行为多态:指多个父类引用指向不同子类对象,调用同一方法时,运行结果不一样。即同样是父类的引用,但是在不同对象下表现出不同的结果。(对应现实生活中,同样是人跑步,年轻人跑的快,老年人跑得慢。)
对于行为多态的用法,记住一个口诀:编译看左边,运行看右边。
编译看左边,即编译时能不能找到该方法,要看左边的引用的类型里有没有该方法;
运行看右边,即运行时运行的是,右边具体的对象中该方法。
d. 两种多态的实现
多态是指对象和行为的多态,对属性(成员变量)不谈多态,接下来是对两种多态的实现:
对于成员变量:编译看左边,运行看左边。即编译时能不能找到该成员变量,要看左边的引用的类型里有没有该成员变量;运行时找的也是左边类型里的变量。
2. 使用多态的好处(为什么)
解耦合具体体现在,父类的引用可以根据需求指向不同的子类对象,这样就可以通过子类来扩展我们需要的内容。也可以随时通过切换指向的对象来舍弃不需要的内容,便于维护代码。
父类作为形参时,可以接受一切子类对象,即可以通过扩展子类对象传递给父类形参,从而接收我们想要的任何信息。
3. 多态的弊端和解决方案
a. 多态不能直接使用子类独有的方法
多态下不能直接使用子类中独有、父类中没有的方法。
b. 解决方案(多态下的类型转换)
即将指向子类的父类变量强制转换回子类变量:
强制类型转换后就能使用子类的特有方法了:
注意事项:
如果子类对象的真实类型和要强转的类型不同,会抛ClassCastException异常。
所以在父类作为形参我们要使用多态来接收子类对象时,最好先使用instanceof关键字来判断一下该子类对象的真实类型:
五、final
1. 概念
拓1. java中的两类变量Java中的变量主要分为两大类:局部变量和成员变量。
成员变量又分为:静态成员变量和实例成员变量。
拓2. java中的两种数据类型
在Java中,数据类型可以分为两大类:基本类型(Primitive Types)和引用类型(Reference Types)。
a. 基本类型:
整数类型:byte, short, int, long
浮点类型:float, double
字符类型:char
布尔类型:boolean
b. 引用类型:
类(Class)
接口(Interface)
数组(Array)
2. final修饰局部变量
修饰局部变量和形参(在方法签名中定义的局部变量),第二次赋值时都会报错:
3. final 修饰的静态成员变量(常量)
在Java中被 final 修饰的静态成员变量 就叫常量。 常量必须赋初值且不能修改。
常量的命名规则和数据库变量的命名规则一致。
常量的作用:
- 记录系统的配置信息。
- 实现硬编码。
- 提高代码的可读性和可维护性。
- 宏替换提高性能。
4. final修饰引用类型变量
指向的地址不可变,地址中的内容可变。
六、抽象abstract
1. 是什么?
- 用abstract修饰的类就是抽象类,抽象类不能有实例对象。
- 用abstract修饰的方法就是抽象方法,抽象方法不能有方法体。
- 抽象方法不能是私有的,必须保证其能被子类访问:
2. 抽象类的特点
- 抽象类中不一定有抽象方法,有抽象方法的一定是抽象类。
- 抽象类中也可以有成员变量、方法、构造器,只是不能创建对象。
- 抽象类的作用就是作为一种特殊的父类让子类继承并实现其中的抽象方法。
- 一个类继承抽象类,必须重写抽象类中的所有抽象方法,否则这个类也必须定义为抽象类。除此之外继承抽象类和继承普通类没有区别。
3. 使用抽象类的好处?
- 设置抽象类和抽象方法,目的是更好的实现行为多态。因为编译器会强制我们在子类中实现抽象方法,从而保证了抽象方法一定能实现行为多态。
- 所以父类中所以子类都要重写的方法最好设置为抽象方法。
4. 抽象类的实际应用场景
a. 设计模板方法
把通用的代码提取出来,设计成一个模板方法,把模板方法中需要自定义的部分,再封装成一个抽象方法,交给子类来重写。
模板方法最好使用final来修饰,目的是防止子类重写。
七、接口
1. 概念
- 接口中定义的变量默认为常量,编译器会自动加上public static final
- 接口中定义的方法默认为抽象方法,编译器会自动加上public abstract
- 接口不能创建对象,接口是用来被其他类实现(implements),实现接口的类被称为实现类。
- 一个类可以实现多个接口(父类是亲爹只能有一个,接口是干爹可以有多个),实现类必须实现所有接口的所有方法,否则这个类必须定义为抽象类。
2. 接口的好处
- 解决了只能单继承的问题。
- 实现了面向接口编程,可以灵活的切换业务。
3. 接口中3种新增的方法
a. 默认方法
- JDK8后才能使用。
- 写在接口中要用default修饰,编译器会默认加上public修饰。
- 只能用实现类的对象来访问。
b. 静态方法
- JDK8后才能使用。
- 写在接口中一定要用static修饰,编译器会默认加上public修饰。
- 能直接使用接口名来调用。
c. 私有方法
- JDK9后才能使用。
- 写在接口中一定要用private修饰。
- 只能用在接口中的默认方法/静态方法/其他私有方法中使用。
d. 为什么要提供这3种新方法?
- 有了默认方法后,能快速给一个接口的所有实现类都新增一个相同的功能。从而减少了系统维护的成本。可以在不修改现有实现类的情况下向接口添加新方法,增强了系统的扩展性。
- 私有方法则是能减少默认方法或静态方法中的重复代码。同时也对默认方法中一些不想暴露的内容进行封装。
- 静态方法则是让接口能脱离实现类,独立完成某些功能。或提供与接口相关的工具方法,如打印接口信息的介绍等。
拓1:私有方法的优点
- 代码重用:减少重复代码。
- 封装性:隐藏不需要暴露给使用者的实现细节。
拓2:静态方法的优点
- 工具性:提供与接口相关的工具方法。
- 独立性:不依赖于任何特定实例。
4. 接口的多继承
a. 是什么?
一个接口可以继承多个接口。
b. 好处
可以把多个接口合并成一个接口,方便实现类去实现(implements后面无需接这么多接口名)。
c. 注意事项
5. 多接口下的方法重名问题
a. 多个接口中的默认方法同名怎么办?
解决方案:
在实现类中,定义一个公共的同名方法,来重写它们。这个时候:三种情况下的调用(无论是两个接口的引用还是子类的引用),均是调用的实现类中定义的默认方法。
实现类中的必须是同名公有方法,因为接口中的默认方法的权限就是public,实现类是在重写该方法所以必须不小于其在接口中的权限。
b. 接口的默认方法和父类中的方法同名怎么办 ?
如果父类中的该方法不是抽象方法且权限是公有的:
在子类中甚至都不需要去实现该方法,因为继承了父类的该方法。
如果父类中的该方法是抽象方法 或 权限不是公有的:
需要在子类中,定义一个公共的同名方法。理由和上面一样。这个时候:三种情况下的调用(无论是父类的引用、接口的引用还是子类的引用),均是调用的子类中定义的默认方法。
c. 重名导致不能多实现的情况
遇到两个接口中重名方法只有返回值不同的情况,一个实现类是无法同时实现这两个接口的。
八、内部类
1. 概念
a. 是什么?
内部类有四种形式:成员内部类、静态内部类、局部内部类、匿名内部类。
b. 使用场景
如汽车类中的发动机类。
2. 成员内部类
a. 概念
b. 创建成员内部类对象
外部类.内部类 变量名 = new 外部类().new 内部类();
成员内部类一定要通过外部类来创建。
c. 成员内部类如何访问外部类成员?
外部类.this.属性 (外部类.this就是指向外部类对象的引用)
3. 静态内部类
为什么需要静态内部类?
这样就可以在不创建外部类的情况下使用内部类。如LinkedList中的node静态内部类。
a. 概念
b. 创建静态内部类对象
外部类.内部类 in = new 外部类.内部类();
静态内部类可通过外部类名直接创建。
c. 静态内部类如何访问外部类成员?
静态内部类访问外部类成员的规则和静态方法访问其他成员的规则一致:能直接访问静态成员,不能直接访问非静态成员(要通过创建外部类对象来访问)。
4. 局部内部类
5. 匿名内部类【重点】
a. 概念
b. 创建匿名内部类
匿名内部类其实是要引用类型的子类,{}是匿名内部类的类体。
查看out里的编译文件我们可发现,编译器将匿名内部类编译为了引用类型的子类。
c. 匿名内部类的使用场景
通常把只需要使用一次的类设计为匿名内部类。
匿名内部类中开发中的真实使用场景
调用jar包中的某些函数的参数为某个指定类,往往这个类只使用一次就是创建一个对象传入该函数中,所以就可以考虑使用内部类,以简化代码。
九、枚举
a. 概念
b. 枚举类的特点
- 枚举类的构造器只能是私有的,不能对外创建对象。主动设置公有构造器会报错。
- 我们只能在枚举类中调用构造器创建对象,在类外用 枚举类名.对象名 调用。
- 枚举类的第一行是常量,是已经建立好的枚举类对象。
- 我们使用枚举类给我们提供的API来操作枚举类。
c. 枚举类使用实例
创建枚举类
调用枚举类对象
d. 枚举类的API
我们使用枚举类给我们提供的API来操作枚举类。
e. 抽象枚举
就是在枚举类第一行写枚举对象时要实现抽象方法。
f. 枚举类的应用场景
- 实现饿汉模式的单例:即在枚举类中只创建一个对象,这样不但高效,且是线程安全的。(但要注意这个单例是最终类,不能继承)
- 用来做参数,限制用户输入。(ƪ(˘⌣˘)ʃ优雅)
十、泛型
a. 概念
泛型就是把数据类型作为变量传入类中,当类中需要指定数据类型时可使用这个变量当作数据类型,这样我们就能指定类中某些数据(如形参)的数据类型了。
泛型不接受基本类型
要使用的话只能用包装类。
泛型的擦除问题
在编译成的class文件中是没有泛型的,就是使用Object类数组来实现的。
b. 泛型类
自定义泛型类:
- 开一个Object类数组,再定义一个size变量保存数组当前已使用大小(其实就是通讯录的实现原理)
- 写取数据的get方法时要强转为泛型类型。
- 泛型类中还可以声明多个类型变量。
- 通过extends还可以限定泛型类的类型
使用自定义泛型类:
- 按规则定义的泛型类和Arraylist数组的使用方式没有区别。
- 如果不指定泛型类型,默认数据为Object类型。取数据时需要强转为变量的类型。
- 在<>在指定数据类型
- 泛型不接受基本类型
c. 泛型接口
d. 泛型方法
泛型方法的定义与使用:
e. 泛型通配符 和 泛型的上下限
在使用泛型的地方,可以代表任何类型。
上限:
就是在?后加一个继承,来限制接收类型的范围,必须是其自己或子类。
下限:
就是在?后加一个super,来限制接收类型的范围,必须是其自己或父类。
------------------------END-------------------------
才疏学浅,谬误难免,欢迎各位批评指正。