关键字:static
static关键字的使用
- static可以用来修饰属性、方法、代码块、内部类
- 使用static修饰属性:静态变量
- 属性,按是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)
- 实例变量:创建了类的多个对象,每个对象都独立拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改
- 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的
- static修饰符的其他说明
- 静态变量随着类的加载而加载。可以通过 “类.静态变量” 的方式进行调用
- 静态变量的加载要早于对象的创建
- 由于类只会加载一次,则静态变量在内存中也会存在一份:存在方法区的静态域中
- 静态属性举例:System.out; Math.PI;
- 属性,按是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)
- 使用static修饰方法:静态方法
-
随着类的加载而加载,可以通过 “类.静态方法” 的方式调用
静态方法 非静态方法 类 yes no 对象 yes yes -
静态方法中,只能调用静态的方法或属性;非静态方法中,既可以调用静态方法或属性也可以调用非静态方法或属性;
-
- static注意点
- 在静态方法中,不能使用this关键字、super关键字
- 关于静态属性和静态方法的使用,从生命周期的角度理解
- 开发中,如何确定一个属性是否要声明为static
- 属性可以被多个对象所共享,不随对象的不同而不同
- 类中的常量也常常声明为static
- 开发中,如何确定一个方法是否要声明为static
- 操作静态属性的方法通常设置为static
- 工具类中的方法,习惯上声明为static,比如:Math、Arrays、Collections
单例(Singleton)设计模式
-
设计模式是在大量的实践中总结的理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索,就像是经典的棋谱,不同的棋局用不同的棋谱。
-
所谓单例设计模式就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的
-
如何实现:饿汉式 与 懒汉式
public class SingletonTest1 { public static void main(String[] args) { Bank bank1 = Bank.getInstance(); Bank bank2 = Bank.getInstance(); System.out.println(bank1 == bank2);//true } } //饿汉式 class Bank{ //1.私有化类的构造器 private Bank() { } //2.内部创建类的对象 //4.要求此对象也必须声明为静态 private static Bank instance = new Bank(); //3.提供公共的静态方法,返回类的对象 public static Bank getInstance() { return instance; } } //懒汉式 class Bank{ //1.私有化类的构造器 private Bank() { } //2.内部创建类的对象 //4.要求此对象也必须声明为静态 private static Bank instance = null; //3.提供公共的静态方法,返回类的对象 public static Bank getInstance() { if(instance == null) { instance = new Bank(); } return instance; } } //另外一种单例 class Bank{ private Bank() { } public static final Bank instance = new Bank(); } -
区分饿汉式和懒汉式
- 饿汉式
- 坏处:对象加载时间过长
- 好处:饿汉式是线程安全的
- 懒汉式
- 好处:延迟对象的创建
- 坏处:目前线程不安全(到多线程时再修改)
- 饿汉式
-
单例模式优点
- 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
理解main方法的语法
- main() 方法作为程序的入口
- main() 方法也是一个普通的静态方法
- main() 方法可以作为我们与控制台交互的方式
类的成员之四:代码块
- 代码块的作用:用来初始化类、对象
- 代码块如果有修饰的话只能使用static
- 分类:静态代码块、非静态代码块
- 静态代码块
- 内部可以有输出语句
- 随着类的加载而执行,而且只执行一次
- 初始化类的信息
- 非静态代码块
-
内部可以有输出语句
-
随着对象的创建而执行
-
每创建一个对象,就执行一次非静态代码块
-
可以在创建对象时,对对象的属性等进行初始化
对属性可以赋值的位置:
- 默认初始化
- 显式初始化
- 构造器初始化
- 有了对象以后,可以通过 “对象.属性” 或 “对象.方法” 的方式进行赋值
- 在代码块中赋值
-
- 关于执行顺序
-
由父及子,静态先行
Leaf extends Mid; Mid extends Root;
Root的静态初始化块 Mid的静态初始化块 Leaf的静态初始化块 Root的普通初始化块 Root的无参数的构造器 Mid的普通初始化块 Mid的无参数的构造器 Mid的带参数构造器,其参数值:尚硅谷 Leaf的普通初始化块 Leaf的构造器
-
关键字:final
-
final 可以用来修饰结构:类、方法、变量
-
final 修饰类:此类不能被其他类所继承
如:String类、System类、StringBuffer类
-
final 用来修饰方法:表明此方法不可以被重写
-
final 修饰变量:此时的变量成为常量
-
final 修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化
-
final 修饰局部变量:
尤其是使用final修饰形参时,表明此形参是一个常量。当调用此方法时,给常量形参赋一个实参,一旦赋值以后就只能在方法体内使用此形参,但不能进行重新赋值
-
-
static final 用来修饰属性:全局常量
抽象类与抽象方法
abstract的使用
- abstract可以用来修饰:类、方法
- abstract修饰类:抽象类
- 此类不能实例化
- 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化全过程)
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关操作
- abstract修饰方法:抽象方法
-
只有方法的声明没有方法体
-
包含抽象方法的类一定是抽象类。反之,抽象类不一定有抽象方法
-
若子类重写了父类中所有的抽象方法后方可实例化
若子类没有重写父类中的所有的抽象方法则此类也必是一个抽象类,需要使用abstract修饰
-
abstract使用注意事项
- abstract不能修饰属性、构造器等结构
- abstract不能用来修饰私有方法、静态方法、final方法、final类
abstract应用举例
-
举例一
public abstract class Vehicle{ public abstract double calcFuelEfficiency();//计算燃料效率的抽象方法 public abstract double calcTripDistance(); //计算行驶距离的抽象方法 } public class Truck extends Vehicle{ public double calcFuelEfficiency(){ } public double calcTripDistance(){ } } public class RiverBarge extends Vehicle{ public double calcFuelEfficiency(){ } public double calcTripDistance(){ } } -
举例二
abstract class GeometricObject{ public abstract double findArea(); } class Circle extends GeometricObject{ private double radius; public double findArea(){ return 3.14 * radius * radius; } } -
举例三:IO流中涉及到的抽象类:InputStream/OutputStream/Reader/Writer。在其内部定义了抽象的read()、write()方法
匿名类
//创建一匿名子类对象
//Person为父类,eat()、breath()为Person中抽象方法
Person p = new Person(){
@Overrride
public void eat() {
System.out.println("吃东西");
}
@Overrride
public void breath() {
System.out.println("好好呼吸");
}
};
模板方法设计模式(TemplateMethod)
- 抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为模式
- 解决的问题
- 当功能内部一部分实现是确定的,一部分实现是不确定的,这时可以把不确定的部分暴露出去,让子类去实现
- 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式
接口(interface)
接口的使用
- 接口使用interface定义
- Java中,接口和类是并列的两个结构
- 如何定义接口:定义接口中的成员
-
JDK7及以前:只能定义全局常量和抽象方法
- 全局常量:public static final,但是可以省略不写
- 抽象方法:public abstract
interface Flyable{ //全局常量 public static final int MAX_SPEED = 7900;//第一宇宙速度 int MIN_SPEED = 1;//省略public static final //抽象方法 public abstract void fly(); //省略了public abstract void stop(); } -
JDK8:除了定义全局常量和抽象方法之外还可以定义静态方法、默认方法
public interface CompareA { //静态方法 //接口中定义的静态方法只能通过接口调用 public static void method1(){ System.out.println("CompareA:北京"); } //默认方法 //通过实现类的对象可以调用接口中的默认方法 //如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法 //如果子类或实现类继承的父类和实现的接口中声明了同名同参数的方法,在子类没有重写此方法的情况下,默认调用的是父类中同名同参数的方法(类优先原则) //如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,则在实现类没有重写此方法的情况下报错(接口冲突),必须在实现类中重写此方法 public default void method2(){ System.out.println("CompareA:上海"); } default void method3(){ //省略public System.out.println("CompareA:上海"); }
-
} //在实现类中调用接口中被重写的方法 CompareA.super.method3(); ```
-
接口中不可以定义构造器,即接口不可以实例化
-
Java中,接口通过让类去实现(implements)的方式使用
如果实现类覆盖了接口中所有的抽象方法,则此实现类就可以实例化
如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
-
Java类可以实现多个接口,弥补了Java单继承性的局限性
格式:class AA extends BB implements CC, DD, EE
-
接口与接口之间可以继承,且可以多继承
-
接口的具体使用体现多态性
-
接口实际上可以看作是一种规范
接口应用举例
class Computer{
public void transferData(USB usb){
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB{
//常量:定义了长、宽、最大最小传输速度等
void start();
void stop();
}
class Flash implements USB{
@Override
public void start() {
System.out.println("U盘开启工作");
}
@Override
public void stop() {
System.out.println("U盘结束工作");
}
}
class Printer implements USB {
@Override
public void start() {
System.out.println("打印机开启工作");
}
@Override
public void stop() {
System.out.println("打印机结束工作");
}
}
接口的应用:代理模式(Proxy)
- 代理模式是Java开发中使用较多的一种设计模式,代理设计就是为其他对象提供一种代理以控制对这个对象的访问
- 应用场景
- 安全代理:屏蔽对真实角色的直接访问
- 远程代理:通过代理类处理远程方法调用(RMI)
- 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
- 分类
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类)
接口的应用:工厂模式
抽象类和接口的异同
- 相同点:不能实例化;都可以包含抽象方法
- 不同点:
- 把抽象类和接口(java7, java8, java9)的定义、内部结构解释说明
- 类:单继承性 接口:多继承性
- 类与接口:多实现
类的成员之五:内部类
- Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B为外部类
- 内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)
- 成员内部类
- 一方面作为外部类的成员
- 调用外部类的结构
- 可以被static修饰
- 可以被4种不同的权限修饰
- 另一方面作为一个类
- 类可以定义属性、方法、构造器
- 可以被final修饰,表示此类不能被继承
- 可以被abstract修饰
- 一方面作为外部类的成员
- 关注如下问题
-
如何实例化成员内部类对象
-
如何在成员内部类中区分调用外部类结构
public class InnerClassTest { public static void main(String[] args) { //创建Dog实例(静态成员内部类) Person.Dog dog = new Person.Dog(); dog.show(); //创建Bird实例(非静态的成员内部类) Person p = new Person(); Person.Bird bird = p.new Bird(); bird.sing(); } } class Person{ String name; int age; public void eat() { System.out.println("人吃饭"); } //静态成员内部类 static class Dog{ String name; public void show() { } } //非静态成员内部类 class Bird{ String name; public Bird() { } public void sing() { Person.this.eat(); //调用外部类的非静态属性 } public void display(String name) { System.out.println(name); //方法的形参 System.out.println(this.name); //内部类的属性 System.out.println(Person.this.name); //外部类的属性 } } public void method() { //局部内部类 class AA{ } } { //局部内部类 class BB{ } } public Person() { //局部内部类 class CC{ } } } -
开发中局部内部类的使用
public class InnerClassTest1 { //开发中少见 public void method() { //局部内部类 class AA{ } } //返回一个实现了Comparable接口的类的对象 public Comparable getComparable() { //创建一个实现了Comparable接口的类:局部内部类 //方式一 class MyComparable implements Comparable{ @Override public int compareTo(Object o) { // TODO Auto-generated method stub return 0; } } return new MyComparable(); //方式二 return new Comparable() { @Override public int compareTo(Object o) { // TODO Auto-generated method stub return 0; } } } } -
成员内部类和局部内部类,在编译以后都会生成字节码文件
格式:成员内部类:外部类$内部类名.class
局部内部类:外部类$数字 内部类名.class -
局部内部类使用注意
public class InnerClassTest{ /* 在局部内部类的方法中(比如show)如果调用外部类所声明的方法(比如method)中的局部变 量(比如num),要求此局部变量声明为final JDK7及之前的版本要求显式声明为final JDK8及之后的版本可以省略final声明 */ public void method(){ //局部变量 int num = 10; class AA{ public void show(){ System.out.println(num); } } } }
-