面向对象三
1. 关键字:static
- static:静态的,随着类的加载而加载、执行。
- static:用来修饰:属性、方法、代码块、内部类
- static修饰的类变量、类方法与不使用static修饰的区别。
- 类变量:类的生命周期内,只有一个。被类的多个实例共享。
注意我们遇到属性或方法时,需要考虑是否声明为static的。
开发中,什么时候需要将属性声明为静态的?
> 判断当前类的多个实例是否能共享此成员变量,且此成员变量的值是相同的。
> 开发中,常将一些常量声明是静态的。比如:Math类中的PI
什么时候需要将方法声明为静态的?
> 方法内操作的变量如果都是静态变量(而非实例变量)的话,则此方法建议声明为静态方法
> 开发中,常常将工具类中的方法,声明为静态方法。比如:Arrays类、Math类
JVM规范:jdk8往后:
栈、堆、方法区(元空间);静态变量(静态域在堆空间中)
静态变量:类中的属性使用static进行修饰。
对比静态变量与实例变量:
① 个数 静态变量:在内存空间中只有一份,被类的多个对象所共享。
实例变量:类的每一个实例(或对象)都保存着一份实例变量。
② 内存位置 静态变量:jdk6及之前:存放在方法区。 jdk7及之后:存放在堆空间
实例变量:存放在堆空间的对象实体中。
③ 加载时机 静态变量:随着类的加载而加载。由于类只会加载一次,所以静态变量也只有一份
实例变量:随着对象的创建而加载。每个对象拥有一份实例变量。
④ 调用者 静态变量:可以被类直接调用,也可以适用对象调用。
实例变量:只能使用对象进行调用。
⑤ 判断是否可以调用 ---> 从生命周期的角度解释
类变量 实例变量 类 yes no 对象 yes yes⑥ 消亡时机
静态变量:随着类的卸载而消亡
实例变量:随着对象的消亡而消亡
2. 单例模式(或单子模式)
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,
并且该类只提供一个取得其对象实例的方法。
- 经典的设计模式有23种
- 解决的问题:在整个软件系统中,只存在当前类的唯一实例。
- 实现方式:饿汉式、懒汉式、枚举类等
- 对比饿汉式和懒汉式
- 饿汉式:“立即加载”,线程安全的。
- 懒汉式:"延迟加载",线程不安全。
优缺点:
饿汉式:(优点)写法简单,由于内存中较早加载,使用更方便、更快。是线程安全的。
(缺点)内存中占用时间较长。
懒汉式:(缺点)线程不安全 (放到多线程章节时解决)
(优点)在需要的时候进行创建,节省内存空间。
3. 理解main()方法
- public static void main(String[] args){}
- 理解1:作为程序的入口;普通的静态方法
- 理解2:如何使用main()与控制台进行数据的交互。
- 命令行:java 类名 "Tom" "Jerry" "123"
4. 类的成员之四:代码块
类中可以声明的结构:属性、方法、构造器;代码块(或初始化块)、内部类
用来初始化类或对象的信息(即初始化类或对象的成员变量),如需修饰只能使用static进行修饰
- 分类:静态代码块、非静态代码块
- 使用频率上来讲:用的比较少。
- 静态代码块:随着类的加载而执行
- 非静态代码块:随着对象的创建而执行
具体使用区别:
具体使用:
静态代码块:
随着类的加载而执行 由于类的加载只会执行一次,进而静态代码块的执行,也只会执行一次 作用:用来初始化类的信息 内部可以声明变量、调用属性或者方法、编写输出语句等操作 静态代码块的执行要先于非静态代码块的执行 如果声明有多个静态代码块,则按照声明的先后顺序执行 静态代码块内部只能调用静态的结构(即静态的属性、方法),不能调用非静态的结构(即非静态的属性、方法)
非静态代码块:
随着对象的创建而执行 每创建当前类的一个实例,就会执行一次非静态代码块 作用:用来初始化对象的信息 内部可以声明变量、调用属性或方法、编写输出语句等操作。 如果声明有多个非静态代码块,则按照声明的先后顺序执行 非静态代码块内部可以调用静态的结构(即静态的属性、方法),也可以调用非静态的结构(即非静态的属性、方法)
总结:
对象的实例变量可以赋值的位置及先后顺序
① 默认初始化 ② 显式初始化 或 ⑤ 代码块中初始化 ③ 构造器中初始化
④ 有了对象以后,通过"对象.属性"或"对象.方法"的方法进行赋值
执行的先后顺序: ① - ②/⑤ - ③ - ④
5. 关键字:final
- 最终的
- 用来修饰:类、方法、变量(成员变量、局部变量)
- 类:不能被继承
- 方法:不能被重写
- 变量:是一个“常量”,一旦赋值不能修改。
final修饰类:表示此类不能被继承。
- 比如:String、StringBuffer、StringBuilder类
final修饰方法:表示此方法不能被重写
- 比如:Object类中的getClass()
final修饰变量:既可以修饰成员变量,也可以修饰局部变量。
- 此时的"变量"其实就变成了"常量",意味着一旦赋值,就不可更改。
final修饰成员变量: 有哪些位置可以给成员变量赋值?
- 显式赋值
- 代码块中赋值
- 构造器中赋值
final修饰局部变量:一旦赋值就不能修改
- 方法内声明的局部变量:在调用局部变量前,一定需要赋值。而且一旦赋值,就不可更改
- 方法的形参:在调用此方法时,给形参进行赋值。而且一旦赋值,就不可更改
final与static搭配:修饰成员变量时,此成员变量称为:全局常量。
- 比如:Math的PI static final double PI = 3.14;
6. 关键字:abstract
- 抽象的
- 用来修饰:类、方法
- 类:抽象类:不能实例化。
- 方法:抽象方法:没有方法体,必须由子类实现此方法。
具体的使用:
abstract修饰类:
此类称为抽象类
抽象类不能实例化。
抽象类中是包含构造器的,因为子类对象实例化时,需要直接或间接的调用到父类的构造器。
抽象类中可以没有抽象方法。反之,抽象方法所在的类,一定是抽象类。
abstract修饰方法:
- 此方法即为抽象方法
- 抽象方法只有方法的声明,没有方法体。
- 抽象方法其功能是确定的(通过方法的声明即可确定),只是不知道如何具体实现(体现为没有方法体)
- 子类必须重写父类中的所有的抽象方法之后,方可实例化。否则,此子类仍然是一个抽象类。
不能使用的场景:
abstract 不能修饰
属性、构造器、代码块等结构。abstract 不能与哪些关键字共用?(自洽)
不能用abstract修饰私有方法、静态方法、final的方法、final的类。
- 私有方法不能重写
- 避免静态方法使用类进行调用
- final的方法不能被重写
- final修饰的类不能有子类
7. 关键字:interface
接口的本质是契约、标准、规范,就像我们的法律一样。制定好后大家都要遵守。
-
interface:接口,用来定义一组规范、一种标准。
-
接口与类的关系
- 实现关系
-
接口中可以声明的结构。
-
属性:使用public static final修饰
-
方法:jdk8之前:只能声明抽象方法,使用public abstract修饰
jdk8中:声明static方法、default方法。
jdk9中:声明private方法。
-
-
不可以声明:
- 构造器、代码块等
格式:class A extends SuperA implements B,C{}
A相较于SuperA来讲,叫做子类
A相较于B,C来讲,叫做实现类。
-
类可以实现多个接口。
-
类针对于接口的多实现,一定程度上就弥补了类的单继承的局限性。
-
类必须将实现的接口中的所有的抽象方法都重写(或实现),方可实例化。否则,此实现类必须声明为抽象类。
-
接口与接口的关系:
- 继承关系,且可以多继承
-
接口的多态性:
- 接口名 变量名 = new 实现类对象;
区分抽象类和接口
共性:
都可以声明抽象方法
都不能实例化
不同:
① 抽象类一定有构造器。接口没有构造器
② 类与类之间继承关系,类与接口之间是实现关系,接口与接口之间是多继承关系
8. 类的成员之五:内部类
什么是内部类?
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类(InnerClass),类B则称为外部类(OuterClass)。
为什么需要内部类?
具体来说,当一个事物A的内部,还有一个部分需要一个完整的结构B进行描述,而这个内部的完整的结构B又只为外部事物A提供服务,不在其他地方单独使用,那么整个内部的完整结构B最好使用内部类。
总的来说,遵循高内聚、低耦合的面向对象开发原则。
内部类的分类:
成员内部类:
直接声明在外部类的里面。
使用static修饰的:静态的成员内
不使用static修饰的:非静态的成员内部类
局部内部类:
声明在方法内、构造器内、代码块内的内部类
匿名的局部内部类
非匿名的局部内部类
关于成员内部类的理解
从类的角度看:
- 内部可以声明属性、方法、构造器、代码块、内部类等结构
- 此内部类可以声明父类,可以实现接口
- 可以使用final修饰
- 可以使用abstract修饰
从外部类的成员的角度看:
- 在内部可以调用外部类的结构。比如:属性、方法等
- 除了使用public、缺省权限修饰之外,还可以使用private、protected修饰
- 可以使用static修饰
> 成员内部类的理解
> 如何创建成员内部类的实例
> 如何在成员内部类中调用外部类的结构
> 局部内部类的基本使用(关注:如何在方法内创建匿名局部内部类的对象)
9. 枚举类:enum
- 使用enum关键字定义的枚举类,默认其父类是java.lang.Enum类
- 使用enum关键字定义的枚举类,不要再显示的定义其父类。否则报错。
常用方法:
String toString(): 默认返回的是常量名(对象名),可以继续手动重写该方法!
static 枚举类型[] values():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值,是一个静态方法
static 枚举类型 valueOf(String name):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
String name():得到当前枚举常量的名称。建议优先使用toString()。
int ordinal():返回当前枚举常量的次序号,默认从0开始
枚举类实现接口的操作
- 情况1:枚举类实现接口,在枚举类中重写接口中的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是同一个方法。
- 情况2:让枚举类的每一个对象重写接口中的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是不同的实现的方法。
10. 注解:Annotation
-
框架 = 注解 + 反射 + 设计模式
-
元注解:对现有的注解进行解释说明。
- @Target:表明可以用来修饰的结构
- 可以通过枚举类型ElementType的10个常量对象来指定
TYPE,METHOD,CONSTRUCTOR,PACKAG
- 可以通过枚举类型ElementType的10个常量对象来指定
- @Retation:用于描述注解的生命周期
- 可以通过枚举类型RetentionPolicy的3个常量对象来指定
SOURCE(源代码)、CLASS(字节码)、RUNTIME(运行时)唯有RUNTIME阶段才能被反射读取到。
- 可以通过枚举类型RetentionPolicy的3个常量对象来指定
- @Documented:表明这个注解应该被 javadoc工具记录。
- @Inherited:允许子类继承父类中的注解
- @Target:表明可以用来修饰的结构
-
如何自定义注解:
@Target({TYPE, FIELD, METHOD,CONSTRUCTOR}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default "hello"; }
注解的应用场景:
- 生成文档相关的注解
- 在编译时进行格式检查(JDK内置的三个基本注解):
@Override: 限定重写父类方法,该注解只能用于方法@Deprecated: 用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择@SuppressWarnings: 抑制编译器警告
- 跟踪代码依赖性,实现替代配置文件功能
11. 包装类的使用
为了使得基本数据类型的变量具备引用数据类型变量的相关特征(比如:封装性、继承性、多态性),给各个基本数据类型的变量都提供了对应的包装类。
如:
byte -> Byte
short -> Short
int -> Integer
long -> Long
float -> Float
double ->Double
char -> Character
boolean -> Boolean
为什么需要转换
在有些场景下,需要使用基本数据类型对应的包装类的对象。此时就需要将基本数据类型的变量转换为包装类的对象。
比如:ArrayList的add(Object obj);Object类的equals(Object obj)对于包装类来讲,既然我们使用的是对象,那么对象是
不能进行+ - * /等运算的。为了能够进行这些运算,就需要将包装类的对象转换为基本数据类型的变量。
如何转换
(装箱)基本数据类型 ---> 包装类:
- ① 使用包装类的构造器
- ② (建议)调用包装类的valueOf(xxx xx)
(拆箱)包装类 ---> 基本数据类型:
- 调用包装类的xxxValue()
jdk5.0新特性:自动装箱、自动拆箱
例如String 与 基本数据类型、包装类之间的转换。
String类型:
① 调用String的重载的静态方法valueOf(xxx xx) ;
② 基本数据类型的变量 + ""
String类型 ---> 基本数据类型、包装类:
调用包装类的静态方法:parseXxx(String str)