面向对象 类(实例) 构成
方法局部变量(方法内定义)
代码块局部变量(代码块内定义)
- 局部变量不属于任何类或实例,总是保存在栈内存中,保存基本变量的值和引用变量的地址
- 局部变量定义后,必须经过显式初始化后才能使用
- 能用代码块局部变量就不用方法局部变量
- 扩大了变量的作用域,这不利于提高程序的内聚性。
- 增大了变量的生存时间,这将导致更大的内存开销。
- 在方法中创建实例 var p = new Person()
实例化
- 个数可变的形参(只能有一个)
- 递归方法
- 基准情形
- 不断推进
- 向已知基准情形推进
- 设计法则
- 假设所有的递归调用都能执行
- 合成效益法则
- 求解同一问题时,切勿做重复性的工作
- 方法重载(Overload)
- 只要形参列表不同 ,可以有多个同名方法
- 两同一不同
- 方法名必须相同("两同"),但参数列表必须不同("一不同")
- 返回值不能区分重载
- toString()方法
- 返回对象的类名,后跟对象的散列码(hash code)
- 所有的Java对象都可以和字符串进行连接运算,系统自动调用Java对象
toString()方法的返回值和字符串进行连接运算
- equals()方法
- Object类提供的一个实例方法,经常被重写使用
- ➢自反性:对任意x, x.equals(x)一定返回true
- ➢ 对称性 : 对任意x和y, 如果y.equals(x)返回 true,则x.equals(y)也返回true
- ➢ 传 递 性 : 对任意x,y,z,如果x.equals(y)返回ture,y.equals(z)返回true,则x.equals(z)一定返回true
- ➢ 一致性:对任意x和y, 如果对象中用于等价比较的信息没有改变, 那么无论调用x.equals(y)多少次,返回的结果应该保持一致,要么一直是true,要么一直是false
- ➢ 对任何不是null的x, x.equals(null)一定返回false
成员
类成员 (必须通过类来访问、调用)
包装类(Wrapper Class)
if (num == value) { System.out.println(“相等”); } else { System.out.println(“不相等”); }
- 实现基本类型变量和字符串之间的转换
- 利用包装类提供的parseXxx(String s)静态方法
( 除Character之外的所有包装类都提供了该方法)
- String str = "123";
int num = Integer.parseInt(str); 将字符串转换为整数类型
- 利用包装类提供的valueOf(String s)静态方法
- String str = "123";
Integer num = Integer.valueOf(str); 将字符串转换为 Integer 对象
- 整数隐式转换为字符串
- int number = 42;
String str = number + “”; // 将int转换为字符串
单例类(Singleton)
一个类始终只能创建一个实例,则这个类被称为单例类
将类的构造器私有化,以防止外部类创建对象
提供一个public静态方法作为该类的访问点,在这个方法中创建实例对象
只能通过 Singleton.getInstance() 方法来获取单例对象
使用一个静态变量来缓存已创建的对象,确保每次调用 getInstance() 方法时都返回相同的实例
必须缓存已经创建的对象,否则该类无法知道 是否曾经创建过对象,也就无法保证只创建一个对象
需要 使用一个成员变量来保存曾经创建的对象,因为该成员变量需要被上 面的静态方法访问,故该成员变量必须使用static修饰
final修饰符
定义
既可以修饰成员变量(包括类变量和实例变量),也可以修饰局 部变量、形参
final修饰的变量不可被改变,一旦获得 了初始值,该final变量的值就不能被重新赋值
final修饰的成员变量必须 由程序员显式地指定初始值 对于final成员变 量,程序当然希望总是能访问到它固定的、显式初始化的值
final 成员变量必须在对象构造过程中确保被初始化。如果在构造器、初始化块中对 final 成员变量进行初始化,确保在使用之前初始化,否则会出现默认初始化的情况
final局部变量
如果final修饰的局部变量在定义时没有指定默认值,则可以在后 面代码中对该final变量赋初始值,但只能一次,不能重复赋值
final只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变
可执行“宏替换”的final变量
定义一个final变量并在声明时指定一个编译时确定的初始值,编译器在编译时将使用宏变量的地方直接替换为其值,以提高性能和减少代码的复杂性
当一个变量满足下面三个条件时,它被认为是一个常量(constant)或直接量(literal)
➢ 使用final修饰符修饰。
➢ 在定义该final变量时指定了初始值。
➢ 该初始值可以在编译时就被确定下来。
Java会使用常量池来管理曾经用过的字符串直接量,例如执行var a=”java”;语句之后,常量池中就会缓存一个字符串”java”;如果程序再次执行var b=”java”;,系统将会让b直接指向常量池中的”java”字符串,因此a==b将会返回true
final方法
final类
不可变类
创建该类的实例后,该实例的实例变量是不可改变的
创建自定义的不可变类
➢ 使用private和final修饰符来修饰该类的成员变量。
➢ 提供带参数的构造器(或返回该实例的类方法),用于根据传入参数来初始化类里的成员变量。
➢ 仅为该类的成员变量提供getter方法,不要为该类的成员变量提供setter方法,因为普通方法无法修改final修饰的成员变量
如果有必要,重写Object类的hashCode()和equals()方法 equals()方法根据 关键成员变量来作为两个对象是否相等的标准,除此之外,还 应该保证两个用equals()方法判断为相等的对象的hashCode() 也相等
缓存实例的不可变类
接口和抽象类
抽象类
接口
接口是从多个相似类中抽象出来的规范,接口不提供任何实现。接口体现的是规范和实现分离的设计哲学
一个Java 源文件里最多只能有一个public接口,如果一个Java源文件里定义 了一个public接口,则该源文件的主文件名必须与该接口名相同
[修饰符] interface 接口名 extends 父接口1, 父接口2… { 零个到多个常量定义(只能是静态常量) 零个到多个抽象方法定义 (只 能是抽象实例方法、类方法、默认方法或私有方法) 零个到多个内部类、接口、枚举定义 零个到多个私有方法、默认方法或类方法定义 接口里不能包含构造器和初始 化块定义 }
修饰符可以是public或者省略,如果省略了public访问控制 符,则默认采用包权限访问控制符,即只有在相同包结构下才 可以访问该接口
接口名应与类名采用相同的命名规则
一个接口可以有多个直接父接口,但接口只能继承接口,不能 继承类
接口里的常量、方法、内部类和内部枚举都是public访问权限
私有方法
静态常量
在 接口中定义成员变量时,不管是否使用public static final修饰符, 接口里的成员变量总是使用这三个修饰符来修饰
在接口中定义的
内部类、内部接口、内部枚举,默认都采用public static两个修饰符
方法,只能是抽象方法、类方法、默认方法或私有方 法,自动为 普通方法增加abstract修饰符
普通方法,接口里的普通方法总是使用public abstract来修饰
成员变量,不管是否使用public static final修饰符, 接口里的成员变量总是使用这三个修饰符来修饰
完全支持多继承
使用接口
接口和抽象类
接口:作为系统与外界交互的规范,规定了实现者必须向外提供哪些服务(方法),以及调用者可以如何调用这些服务。在程序中使用接口时,它是多个模块间的耦合标准。在多个应用程序之间使用接口时,它是多个程序之间的通信标准。接口类似于整个系统的“总纲”,制定了系统各模块应该遵循的标准。因此,一旦接口被改变,对整个系统或其他系统的影响将是辐射式的,可能导致系统中大部分类都需要修改
抽象类:作为系统中多个子类的共同父类,体现了一种模板式设计。抽象类是系统实现过程中的中间产品,它已经实现了系统的部分功能(那些已提供实现的方法)。然而,这个中间产品依然不能作为最终产品,需要有进一步的完善
相同
差别
➢ 接口里只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法
➢ 接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量。
➢ 接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
➢ 接口里不能包含初始化块;但抽象类则完全可以包含初始化块
➢ 一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足
Lambda表达式 Lambda表达式基本结构
形参列表: 允许省略类型,如果只有一个参数可以省略括号
箭头符号 ->:将参数列表与Lambda表达式的主体分隔开
主体: 包含Lambda表达式要执行的语句
如果Lambda表达式只有一个语句,可以省略花括号 {}
如果Lambda表达式只包含一条返回语句,可以省略 return 关键字 Lambda表达式需要返回值,而它的代码块中仅有一条 省略了return的语句,Lambda表达式会自动返回这条语句的 值
Eatable eat = () -> System.out.println(“Eating”); // 一个没有参数的Lambda表达式 eat.eat();
Flyable fly = distance -> System.out.println(“Flying “ + distance + “ miles”); // 一个带有一个参数的Lambda表达式 fly.fly(1000);
Addable add = (a, b) -> a + b; // 一个带有两个参数的Lambda表达式 System.out.println(add.add(5, 3));
Lambda表达式与函数式接口
特点
目标类型:Lambda 表达式的类型,也就是它的目标类型,必须是函数式接口
函数式接口:函数式接口代表只有一个抽象方法的接口。Lambda 表达式实现的是这个唯一的抽象方法
@FunctionalInterface注解:这个注解放在接口定义前,用于告诉编译器该接口必须是函数式接口。如果接口不符合函数式接口的条件,编译器会报错
限制
确保 Lambda 表达式的目标类型是函数式接口
➢ 将Lambda表达式赋值给函数式接口类型的变量 Runnable r = () -> System.out.println(“Running…”);
- ➢ 将Lambda表达式作为函数式接口类型的参数传给某个方法
public void execute(Runnable r) { r.run(); }
execute(() -> System.out.println(“Executing…”));
- ➢ 使用函数式接口对Lambda表达式进行强制类型转换
Object obj = (Runnable)() -> System.out.println(“Converting to Runnable…”);
在Lambda表达式中使用var
方法引用与构造器引用
与匿名内部类的联系和区别
使用Lambda表达式调用Arrays的类方法 面向接口编程 简单工厂模式
对创建对象过程的封装。通过简单工厂,可以将对象的创建逻辑集中管理,提高系统的灵活性
命令模式
三大特征 封装
目的
隐藏和封装
private(当前类访问权限):只能在当前类的内部被访问
default(包访问权限):不使用任何访问控制符 修饰 访问控制的成员或外部 类可以被相同包下的其他类访问
protected(子类访问权限):那么这 个成员既可以被同一个包中的其他类访问,也可以被不同包中 的子类访问,通常是希望其子类来重写这个方法。
public(公共访问权限):如果一个成员(包括成员变量、方法和构造器等)或者一个外 部类使用public访问控制符修饰,那么这个成员或外部类就可 以被所有类访问
访问控制符的使用
类里的绝大部分成员变量都应该使用private修饰,只有一些 static修饰的、类似全局变量的成员变量,才可能考虑使用 public修饰。
有些方法只用于辅助实现该类的其他 方法,这些方法被称为工具方法,工具方法也应该使用private 修饰。
如果某个类主要用做其他类的父类,该类里包含的大部分方法 可能仅希望被其子类重写,而不想被外界直接调用,则应该使 用protected修饰这些方法
希望暴露出来给其他类自由调用的方法应该使用public修饰。
多态(Polymorphism)
- 通过引用变量来访问其包含的实例变量时,系统总是试图访问
它编译时类型所定义的成员变量,而不是它运行时类型所定义的成 员变量。
方法重写(override)
向上转型
Java允许将子类对象直接赋给父类引用变量,无需任何显式类型转换。这个过程被称为向上转型(Upcasting),向上转型由系统自动完成
引用变量的强制类型转换
继承
类的继承
每个子类只有一个直接父类
public class 子类名 extends 父类名 { // 子类的成员变量和方法 }
子类只能从被扩展的父类获得成员变量、方法和内部类(包括 内部接口、枚举),不能获得构造器和初始化块。
继承适用于那些具有”is-a”关系的类,即子类是父类的一种特例。
重写父类的方法
调用父类构造器
继承与组合(类复用)
父类应有良好的封装性,不会被子类随意改变
尽量隐藏父类的内部数据
不要让子类可以随意访问、修改父类的方法
尽量不要在父类构造器中调用将要被子类重写的方法
何时需要从父类派生新的子类
利用组合实现复用