static 关键字
如果想让一个成员变量被类的所有实例所共享,就用 static 修饰即可,称为类变量(或类属性)
可修饰结构:属性、方法、代码块、内部类
对比静态变量和实例变量
- 个数
- 静态变量:只有一份,被多个对象共享;
- 实例变量:每一个对象都保存着一份实例变量;
- 内存位置
- 静态变量:存放在堆空间;
- 实例变量:存放在堆空间的对象实体中;
- 加载时机
- 静态变量:随着类的加载而加载;
- 实例变量:随着对象的创建而加载;
- 调用者
- 静态变量:可以被类直接调用,也可以使用对象调用;
- 实例变量:只能使用对象进行调用;
- 判断是否可以调用(从生命周期的角度解释)
类变量 | 实例变量 | |
---|---|---|
类 | ✓ | ✕ |
对象 | ✓ | ✓ |
- 消亡时机
- 静态变量:随着类的卸载而消亡;
- 实例变量:随着对象的消亡而消亡;
static 修饰方法
也可以称为类方法或静态方法。它随着类的加载而加载,可以通过 类.静态方法
的方式直接调用。
静态方法可以调用其它静态的结构,不可以调用非静态的结构(实例对象、方法、构造器),同时静态方法中无法使用 this 和 super 关键字。
单例设计模式
设计模式概述
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。经典的设计模式共有23种
单例模式概述
采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
实现单例模式
饿汉式
- 私有化类的构造器;
- 在类内部创建当前类的静态实例属性;
- 使用静态的 get() 方法获取当前类的实例。
class Bank{
private Bank(){}
private static Bank instance = new Bank();
public static Bank getInstance(){
return instance;
}
}
懒汉式
- 私有化类的构造器;
- 在类内部声明当前类的静态实例属性;
- 在静态的 get() 方法内创建当前类的实例,使用 get() 方法时即可获取当前类的实例。
class Bank{
private Bank(){}
private static Bank instance = null;
public static Bank getInstance(){
if(instance == null){
instance = new Bank();
}
return instance;
}
}
对比两种方式
- 特点
- 饿汉式:立即加载,随着类的加载,实例就创建了;
- 懒汉式:延迟加载,在需要使用的时候进行创建
- 优点
- 饿汉式:写法简单,由于在内存种较早加载,使用更方便、更快,是线程安全的;
- 懒汉式:在需要的时候进行创建,节省内存空间;
- 缺点
- 饿汉式:内存中占用时间较长;
- 懒汉式:线程不安全;
main 方法
public static void main(String args[]){}
理解1:看作是一个普通的静态方法(可以通过控制台调用);
理解2:看作是程序的入口,格式是固定的。
代码块
代码块用来初始化类或对象的信息,由一对 {}
构成。其可以使用的修饰符只有 static ,故可以分为静态代码块和非静态代码块。其使用规范与方法大致相同。
-
静态代码块
- 随着类的加载而执行且只会执行一次,用来初始化类的信息。
-
非静态代码块
- 随着对象的创建而执行且每当实例化一个对象时就会执行一次,用来初始化对象的信息。
补充知识
可以给类的非静态属性赋值的方式及其执行顺序为:
- 默认初始化
- 显式初始化 或 代码块中初始化
- 构造器初始化
- 创建对象后,通过
对象.属性
或对象.方法
进行赋值。
final 关键字
final 可以理解为最终的,可以用来修饰类、方法和变量。
- final 修饰类:表示该类不能被继承;
- final 修饰方法:表示此方法不能被重写;
- final 修饰变量:表示该值不能被更改,相当于一个常量。
- 对于成员变量,只能通过显式赋值、代码块赋值以及构造器赋值。与 static 搭配时表示该成员变量为全局常量。
抽象(abstract)
子类拥有共同特征,但在父类中却无法给出具体的实现,所以父类在声明这些方法时就只有方法签名,没有方法体。没有方法体的方法称为抽象方法,而包含抽象方法的类必须是抽象类。
abstract 修饰类
-
abstract 关键字修饰的类为抽象类,抽象类不能被实例化;
-
抽象类中是包含构造器的,子类对象实例化时会调用该构造器;
-
抽象类中可以没有抽象方法。
abstract 修饰方法
- abstract 关键字修饰的方法为抽象方法,抽象方法只有方法声明,没有方法体;
- 子类必须重写父类中的所有抽象方法后才可以实例化,否则该子类仍是一个抽象类。
补充
-
abstract 不能修饰属性、构造器、代码块等;
-
abstract 不能用来修饰私有方法、静态方法、final 的方法和类。
接口(interface)
接口是规范,定义的是一组规则。继承是 is-a 的关系,而接口是 has-a (能不能)的关系。
接口结构
- 可以声明:
- 属性:必须使用
public static final
修饰(可以省略); - 方法:jdk8之前只能声明抽象方法,用
public abstract
修饰(可以省略);jdk8可以声明静态方法、默认方法;jdk9可以声明私有方法。
- 属性:必须使用
- 不可以声明:构造器、代码块等。
接口的实现
接口和类之间为实现关系,格式为:class A extends SuperA implements B, C
(一个类可以实现多个接口)。
类针对于接口的多实现,一定程度上弥补了类单继承的局限性。
实现类必须将接口中所有的抽象方法都实现才可以实例化,否则该类要声明为抽象类。
接口之间可以多继承,最终的实现类需要实现所有接口的方法;
接口的多态性
格式:接口名 变量名 = new 实现类对象
public class USBTest {
public static void main(String[] args) {
Computer computer = new Computer();
// 1.创建接口实现类的对象
Printer printer = new Printer();
computer.transferData(printer);
// 2.创建接口实现类的匿名对象
System.out.println("\n*****************");
computer.transferData(new Camera());
// 3.创建接口实现类的匿名对象
System.out.println("\n*****************");
USB usb1 = new USB() {
@Override
public void start() {
System.out.println("U盘开始工作...");
}
@Override
public void stop() {
System.out.println("U盘停止工作...");
}
};
computer.transferData(usb1);
// 4.创建接口匿名实现类的匿名对象
System.out.println("\n*****************");
computer.transferData(new USB() {
@Override
public void start() {
System.out.println("扫描仪开始工作...");
}
@Override
public void stop() {
System.out.println("扫描仪开始工作...");
}
});
}
}
class Computer{
public void transferData(USB usb){
System.out.println("设备连接成功...");
usb.start();
System.out.println("数据传输...");
usb.stop();
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开始工作...");
}
@Override
public void stop() {
System.out.println("打印机停止工作...");
}
}
class Camera implements USB{
@Override
public void start() {
System.out.println("照相机开始工作...");
}
@Override
public void stop() {
System.out.println("照相机停止工作...");
}
}
interface USB{
void start();
void stop();
}
区分抽象类和接口
- 共性:
- 都可以声明抽象方法;
- 都不能实例化
- 不同:
- 抽象类一定有构造器而接口没有;
- 类和接口的本质不同导致的不同。
内部类(InnerClass)
概述
将一个类 A 定义到另一个类 B 里边,则称 A 为内部类(InnerClass),B 为外部类(OuterClass)。
具体来说,当一个事物 A 的内部,还有一个部分需要一个完整的结构 B 进行描述,而这个内部的完整的结构 B 又只为外部事物 A 提供服务,不在其它地方单独使用,那么整个内部的完整结构 B 最好使用内部类。
内部类的分类
- 成员内部类:直接声明在外部类的里面
- 使用 static 修饰的:静态的成员内部类
- 不使用 static 修饰的:非静态的成员内部类
- 局部内部类:声明在方法内、构造器内、代码块内的内部类
- 匿名的局部内部类
- 非匿名的局部内部类
成员内部类
从类的角度来看:
- 内部可以声明属性、方法、构造器、代码块、内部类等结构;
- 此内部类可以声明父类,可以实现接口;
- 可以使用 final 、abstract 关键字修饰
从外部类成员的角度来看:
- 在内部可以调用外部类的结构,例如属性、方法等;
- 所有的权限修饰符都可以使用(一个 .java 文件中只有一个类可以用 public 修饰,其它类只能用缺省修饰);
- 可以使用 static 修饰
创建成员内部类的实例:
// Dog 类为 Person 类的静态内部类,Bird 类为非静态内部类
//1.创建 Person 的静态的成员内部类的实例
Person.Dog dog = new Person.Dog();
dog.eat();
//2.创建 Person 的非静态的成员内部类的实例
Person p1 = new Person();
Person.Bird bird = p1.new Bird();
bird.eat();
在内部类中调用外部类的同名属性和方法时,通过 外部类.this.方法\属性
的方式进行调用。
局部内部类
开发中的场景:
public Comparable getInstance(){
//方式1:提供了接口实现类的匿名对象
class MyComparable implements Comparable{
@Override
public int compareTo(Object o){
return 0;
}
}
return new MyComparable();
//方式2:提供了接口的匿名实现类的对象
Comparable c = new Comparable(){
@Override
public int compareTo(Object o){
return 0;
}
};
return c;
//方式3:提供了接口的匿名实现类的匿名对象
return new Comparable(){
@Override
public int compareTo(Object o){
return 0;
}
};
}
通过编译文件理解内部类
枚举类(Enum)
概述
枚举类本质上也是一种类,只不过是这个类的对象是有限的、固定的几个,不能让用户随意创建。
在开发中,如果针对于某个类的实例对象是确定个数的,则推荐将子类声明为枚举类。如果枚举类的实例只有一个,可以看作是单例的实现方式。
使用方法
在 jdk5 之后可以通过 enum
关键字来声明枚举类。其默认父类是 java.lang.Enum
,不允许再显式的声明其父类。
enum Season{
// 1.必须在枚举类的开头声明多个对象,对象之间用 "," 隔开
SPRING("春天", "春暖花开"),
SUMMER("夏天", "夏日炎炎"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "白雪皑皑")
;
// 2.声明当前类的对象的实例变量,使用 private final 修饰
private final String SEASON_NAME; // 季节名称
private final String SEASON_DESC; // 季节描述
// 3.私有化类的构造器
Season(String SEASON_NAME, String SEASON_DESC) {
this.SEASON_NAME = SEASON_NAME;
this.SEASON_DESC = SEASON_DESC;
}
// 4.提供实例变量的 get 方法
public String getSEASON_NAME() {
return SEASON_NAME;
}
public String getSEASON_DESC() {
return SEASON_DESC;
}
}
Enum 类中常用方法
String toString ( ):默认返回的是常量名 (对象名),可以继续手动重写该方法!
static 枚举类型[ ] values ( ):返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值,是一个静态方法。
static 枚举类型 valueOf (String name):可以把一个字符串转为对应的枚举类对象(可以理解为在枚举类对象中查找名称为该字符串的)。要求字符串必须是枚举类对象的"名字"。如不存在该名字的,会有运行时异常(IllegalArgumentException)。
String name ( ):得到当前枚举常量的名称。建议优先使用 toString ( ) 。
int ordinal ( ):返回当前枚举常量的次序号,默认从 0 开始。
枚举类实现接口
情况1:枚举类实现接口,在枚举类中重写接口中的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是同一个方法。
情况2:在枚举类的每一个对象重写接口中的方法。当通过不同的枚举类对象调用此方法时,执行的是不同的方法。
注解(Annotation)
概述
注解可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,还可以添加一些参数值,这些信息被保存在 Annotation 的 “name=value” 对中。
注解可以在类编译、运行时进行加载,体现不同的功能。
元注解
对现有注解进行解释说明的注解。
- @Target:用于描述注解的使用范围,可以通过枚举类型 ElementType 的 10 个常量对象来指定;
- @Retention:用于描述注解的声明周期,可以通过枚举类型 RetentionPolicy 的 3 个常量对象来指定;
- @Documented:表明这个注解应该被 javadoc 工具记录;
- @Inherited:允许子类继承父类中的注解。
Junit 单元测试
概述
黑盒测试:不需要写代码,输入值看程序是否能够输出期望的值。
白盒测试:需要写代码,关注程序具体的执行流程。
使用方法
导入 Junit 包后在待测试的方法上添加 @Test 注解后可以单独允许方法。
包装类
概述
为了使基本数据类型的变量具备引用数据类型变量的相关特征(例如:封装性、继承性、多态性),Java 针对基本数据类型定义了相应的引用类型:包装类(封装类)。
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
包装类和基本数据类型间的转换
-
(装箱)基本数据类型 --> 包装类:
-
使用包装类的构造器(jdk9之后被弃用)
-
使用包装类的 valueOf() 方法(推荐)
-
-
(拆箱)包装类 --> 基本数据类型:
- 调用包装类的xxxValue()方法
jdk5之后可以自动装箱和自动拆箱,例如:
// 自动装箱
Integer i1 = 10;
// 自动拆箱
int i2 = i1;
String 类型与包装类或基本数据类型间的转换
- 基本数据类型、包装类 --> String 类型:
- 调用 String 的重载的静态方法 valueOf(xxx xx)
- 使用
基本数据类型 + ''
的方式即可
- String 类型 --> 基本数据类型、包装类:
- 调用包装类的静态方法:parseXxx( )