@[toc]
static关键字
为什么要用static
当编写一个类时,就是在描述其对象的属性和行为,而并没有产生实际上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。
我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有国家是中国这个属性,每个中国人都共享同一个中国属性,就不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。
PS:把所有对象共同的属性或方法提取出来。
static关键字的使用
1. static :静态的。
2. static 可以修饰:属性、方法、代码块、内部类。
修饰后的静态类结构会随着类的加载而加载。
3. 使用static修饰属性:
被修饰后的属性就叫做:静态属性(静态变量、类变量)
3.1 属性的分类:
按照是否使用static修饰,又分为:静态属性(类变量)
和非静态属性(实例变量)
- 静态属性(类变量):创建类的多个对象后,多个对象共享同一个静态属性。当通过某一个对象修改了静态属性,会导致其他对象调用此静态属性时,是修改过的。
- 非静态属性(实例变量):创建类的多个对象后,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值被修改。
3.2 static修饰属性的其他说明:
- 可以通过
类.静态属性
或对象.静态属性
的方式调用静态属性。 - 静态属性随着类的加载而加载。所以,静态属性的加载早于对象的创建。
- 由于类只会加载一次,则静态属性在内存中也只会存在一份,存在于方法区的静态域中。
- 类能调用静态属性(类变量)。因为静态属性和类同时加载。
类不能调用非静态属性(实例变量)。因为类加载时,非静态属性还没加载,非静态变量跟对象一起加载。
对象能调用静态属性(类变量)。因为先加载类,后加载对象,对象在类之后加载。
对象能调用非静态属性(实例变量)。因为对象和非静态属性同时加载。
3.3 静态属性举例:
System.out 、Math.PI
3.4 再提一嘴内存解析:
- 栈:局部变量。
- 堆:new出来的结构,对象、数组。
- 方法区:类的加载信息、静态域、常量池。
4. 使用static修饰方法:
被修饰后的方法就叫做:静态方法
- 可以通过
类.静态方法
或对象.静态方法
的方式进行调用。 - 随着类的加载而加载。
- 类能调用静态方法。
类不能调用非静态方法。
对象能调用静态方法。
对象能调用非静态方法。
- 静态方法中,只能调用静态的方法或属性。
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性。
5. static注意点:
- 在静态的方法内,不能使用
this
和super
关键字。因为这俩关键字都是基于当前对象的,但对象的加载晚于静态方法。 - 关于静态属性和静态方法的使用,要从生命周期的角度考虑。静态结构随类的加载而加载,随类的消亡而消亡。
6. 开发中,如何确定一个属性是否要声明为static:
- 属性可以被多个对象共享,不会随着对象的不同而不同。
- 类中的常量也常常声明为static。final static ...
开发中,如何确定一个方法是否要声明为static:
- 操作静态属性的方法,通常设置为static。
- 工具类中的方法,习惯上声明为static。比如:Math、Arrays、Collections
单例设计模式
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局用不同的棋谱,免去我们自己再思考和摸索。等同于 ”套路”。
类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为 private,这样,就不能用 new 操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
单例设计模式的优点:
由于单例模式只产生一个实例,减少了系统性能的开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
/*
* 单例的饿汉式实现
*
*/
class Singleton{
//1.私有化类的构造器
private Singleton() {
}
//2.内部创建类的对象
//此对象必须声明为静态的
private static Singleton single = new Singleton();
//3.提供公共的静态的方法,返回类的对象。
public static Singleton getInstance() {
return instance;
}
}
/*
* 单例的懒汉式实现
*
*/
class Singleton{
//1.私有化类的构造器
private Singleton() {
}
//2.声明当前类对象,没有初始化。
//此对象也必须声明为 static 的
private static Singleton single; // 默认 == null;
//3.声明 public、static 的返回当前类对象的方法
public static Singleton getInstance() {
if(single == null) {
single = new Singleton();
}
return single;
}
}
区分饿汉式和懒汉式:
- 饿汉式:
坏处:对象加载时间过长。
好处:饿汉式是线程安全的。
- 懒汉式:
好处:延迟对象的创建。
坏处:目前的写法,会线程不安全。(到多线程内容时再修改)
单例设计模式的应用场景
- 网站的计数器,一般也是单例模式实现,否则难以同步。
- 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
- 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。
- Application 也是单例的典型应用
- Windows 的 Task Manager (任务管理器)就是很典型的单例模式
- Windows 的 Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
main()方法
由于 Java 虚拟机需要调用类的 main()方法,所以该方法的访问权限必须是 public,又因为 Java 虚拟机在执行 main()方法时不必创建对象,所以该方法必须是 static 的,该方法接收一个 String 类型的数组参数,该数组中保存执行 Java 命令时传递给所运行的类的参数。
又因为 main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员。
main()方法使用说明:
- main()方法作为程序的入口。
- main()方法也是一个普通的静态方法。
- main()方法可以作为我们与控制台交互的方法。(之前只知道 Scanner )
代码块
1. 代码块的作用: 用来初始化类
(静态代码块)或对象
(非静态代码块)。
2. 代码块如果想修饰,只能使用: static
。
3. 分类: 静态代码块和非静态代码块。
4. 静态代码块:
- 内部可以有输出语句。
- 随着类的加载而执行,而且只能执行一次。
- 作用:初始化类的信息。
- 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行。
- 静态代码块的执行要优先于非静态代码块的执行。
- 静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构。
5. 非静态代码块:
- 内部可以有输出语句。
- 随着对象的创建而执行。
- 每创建一个对象,就执行一次非静态代码块。
- 作用:可以在创建对象时,对对象的属性等进行初始化。
- 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行。
- 非静态代码块内可以调用静态的属性、静态的方法,和非静态的属性、非静态的方法。
属性赋值
属性可以赋值的位置:
- 默认初始化
- 显示初始化
- 构造器初始化
- 有了对象后,可以通过 “对象.属性” 或 “对象.方法” 的方式,进行赋值
- 在代码块中赋值
这是学过的5种赋值方式。
执行的先后顺序:
① → ② / ⑤ → ③ → ④
② ⑤ 分别是:显示初始化和在代码块中赋值。他俩的执行顺序是平行的,所以取决于他俩在代码中的先后位置,后执行的会把前面执行的值覆盖掉。
final关键字
1. final:最终的
2. final 修饰类:
此类不能
被其他类所 继承
。 比如:String类、System类、StringBuffer类。
3. final 修饰方法:
此方法不能
被 重写
。 比如:Object 类中 getClass();
4. final 修饰变量:
此时变量就成为了一个 常量
。
- final 修饰属性:可以考虑赋值的位置有,显式初始化、代码块中初始化、构造器中初始化。
- final 修饰局部变量:使用 final 修饰形参时,表明此形参是一个常量。
当调用此方法时,一旦给常量形参赋一个实参,那么就只能在方法体内使用此形参,但不能进行重新赋值。
static final 用来修饰属性: 全局常量
抽象类与抽象方法
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
abstract关键字的使用
1. abstract :抽象的 。
2. abstract 可以修饰: 类、方法 。
3. abstract 修饰类:
被修饰的类叫做:抽象类
。
- 抽象类不能实例化。
- 抽象类中一定有构造器,哪怕不显示的写,也有默认的,只要是类就一定有构造器。便于子类实例化时调用(涉及:子类对象实例化的全过程)。
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关操作。
4. abstract 修饰方法:
被修饰的方法叫做:抽象方法
。
- 抽象方法只有方法的声明,没有方法体。
- 包含抽象方法的类,一定是一个抽象类。但是,抽象类中可以没有抽象方法。
- 若子类重写了父类中的所有抽象方法后,则此子类可以实例化。
若子类没有重写父类中的所有抽象方法,则此子类也是个抽象类,需要 abstract 修饰。
abstract 使用上的注意点:
- abstract 不能修饰:属性、构造器、代码块。
- abstract 不能修饰:私有方法、静态方法、final 方法、final 类。(原因:private方法不能重写,都不能继承,哪来的重写;static 方法不能被重写,随类加载的不能重写,随对象加载的才能;final方法不能被重写,而abstract必须重写后才能造对象,矛盾了;final类不能被继承,abstract类不能造对象,只有继承后的非abstract类才可以)
接口
概述
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java 不支持多重继承(只能多层继承)。有了接口,就可以达到多重继承的效果。
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有 is-a 的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3 机、手机、数码相机、移动硬盘等都支持 USB 连接。
接口就是规范,定义的是一组规则,体现了现实世界中 “如果你是/要…则必须能…” 的思想。继承是一个"是不是"的关系,而接口实现则是"能不能"的关系。
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
接口的使用
1. 接口使用 interface
来定义
2. Java中,接口和类是并列的两个结构
3. 如何定义接口:定义接口中的成员
3.1 JDK7及以前:只能定义全局常量和抽象方法 。
- 全局常量:
public static final
。书写时,默认就有,可以省略不写。 - 抽象方法:
public abstract
。可省略。
3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法 。
4. 接口中不能定义构造器!!!意味着接口不可以实例化
5. 接口通过让类去实现的方式来使用
- 如果实现类重写了接口中的
所有
抽象方法,则此实现类就可以实例化。 - 如果实现类没有重写接口中的所有方法,则此实现类仍为一个抽象类。
PS:子类的方法覆盖父类的方法,类与类间这叫重写,即子类重写父类方法;类的方法覆盖接口的抽象方法,类对接口这叫实现,即类实现接口方法。不过可以省事,都叫重写就行。
6. Java类可以实现多接口 —— 弥补了Java单继承性的局限性
格式,先写 extends
,后写 implements
:
class AA extends BB implements CC,DD,EE {}
7. 接口与接口之间可以继承,而且可以多继承
8. 接口的使用
- 接口与实现类之间存在多态性。
- 接口,实际上就是定义了一种规范。
- 接口的主要用途就是被实现类实现。(面向接口编程)
- 接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口时一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7及之前),而没有变量和方法的实现。
Java 8中关于接口的改进
Java 8中,可以为接口添加静态方法
和默认方法
。从技术角度来说,这是完全合法的,只是看起来违反了接口作为一个抽象定义的理念。
- 静态方法:使用
static
关键字修饰。可以通过接口直接调用静态方法。
格式:
public static 返回值类型 方法名() {}
- 默认方法:使用
default
关键字修饰。可以通过实现类对象来调用。
格式:
public default 返回值类型 方法名() {}
PS:此时的 default 可不是权限修饰符,它就是一种固定的写法。
静态方法和默认方法的权限修饰符都是 public
,写法上 public 可省略 。
接口使用知识点补充
- 接口中定义的
静态方法
,只能通过接口来调用,不能通过类或对象调用。
调用格式:
接口.静态方法();
- 通过 "实现类" 的对象,可以调用接口中的
默认方法
。如果 "实现类" 重写了接口中的默认方法,则调用的就是重写以后的方法。
下面都是极端情况,项目中一般不会定义同名方法
- 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。这叫 —— 类优先原则 。
- 如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错!这叫 —— 接口冲突 。所以必须在实现类中重写此方法,或实现类继承的父类中重写过。
- 如何在子类(或实现类)的方法中调用父类、接口中被重写的方法:
方法(); // 调用自己定义的重写的方法
super.方法(); // 调用父类中重写的方法,这个super代表父类
接口.super.方法(); // 调用接口的中默认方法,这个super是固定写法,没有实际含义!!!
内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
1. Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
2. 内部类的分类:
- 成员内部类(静态、非静态)
- 局部内部类(方法内、代码块内、构造器内)
3. 成员内部类:
一方面,作为外部类的成员:
- 调用外部类的结构
- 可以被 static 修饰
- 可以被4种权限修饰符修饰
另一方面,作为一个类:
- 类内可以定义属性、方法、构造器等
- 可以被 final 修饰,表示此类不能被继承
- 可以被 abstract 修饰
4. 关注如下3个问题:
- 如何实例化成员内部类的对象
// 创建静态的成员内部类的实例
外部类.内部类 实例 = new 外部类.内部类();
// 创建非静态的成员内部类的实例
//外部类.内部类 实例名 = new 外部类.内部类(); 这是错误的!
外部类 变量 = new 外部类();
外部类.内部类 实例 = 变量.new 内部类();
- 如何在成员内部类中区分调用外部类的结构
this.属性 // 内部类的属性
外部类.this.属性 // 外部类的属性
例:
public void display(String name){
System.out.println(name); //方法的形参
System.out.println(this.name); //内部类的属性
System.out.println(Person.this.name); //外部类的属性
}
- 局部内部类的使用