- 作用:用来初始化类、对象
- 代码块如果有修饰的化,只能使用static
代码块的分类:
- 内部可以有输出语句
- 随着类的加载而执行,而且只执行一次
- 作用: 初始化类的信息,比如,对静态变量赋值
- 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
- 静态代码块的执行优先于非静态代码块的执行
- 静态代码块内只能调用静态的属性、方法,不能调用非静态的结构
- 内部可以有输出语句
- 随着对象的创建而执行
- 每创建一次对象,就执行一次非静态代码块
- 作用: 可以在创建对象时,对对象的属性等进行初始化
- 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
- 非静态代码块可以调用静态 or 非静态的属性、方法
总结: 静态先行【代码块->static main】,由父及子
注: 对属性可以赋值的位置:
- 默认初始化
- 显式初始化
- 构造器初始化
- 有了对象以后,可以通过"对象.属性" 或 "对象.方法"的方式,进行赋值
- 在代码块中赋值
- 先后执行顺序: 1-> 2 / 5 -> 3 -> 4
- Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B为外部类
内部类可以分为成员内部类(静态、非静态)和局部内部类(方法内、代码块内、构造器内)
局部内部类是定义在一个方法、一个代码块、一个构造器内的类
特点:
问题:
1.如何实例化成员内部类的对象?
静态会随着类的加载而加载
//创建Dog实例(静态的成员内部类)
PersonB.Dog dog = new PersonB.Dog();
dog.show();
//创建Bird实例(非静态的成员内部类)
// PersonB.Bird bird = new PersonB.Bird();
PersonB p = new PersonB();
PersonB.Bird bird = p.new Bird();
bird.sing();
2.如何在成员内部类中区分调用外部类的结构
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(PersonB.this.name);//外部类的属性
}
3.开发中局部内部类的使用
一个方法会返回会一个局部内部类对象,其中,局部内部类实现了某个接口,具体使用可以先定义一个实现类,或者直接返回匿名类的匿名对象
static 可以用来修饰:属性、方法、代码块、内部类【主要用来修饰类的内部结构,随着类的加载而加载:类加载中->构造器->对象,所以不包括构造器】
- 属性按是否用static修饰,又分为 静态属性 和 非静态属性(实例变量)
- 实例变量: 当创建了类的多个对象后,每个对象都独立的拥有一套类中的非静态属性;因此,当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改
- 静态变量:创建了类的多个对象后,多个对象共享同一个静态变量;当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时是修改过的
- 静态变量随着类的加载而加载——早于对象的创建——可以直接用 "类名.静态变量="的方式进行调用,如初始化静态变量
- 由于类只会加载一次,则静态变量在内存中也只会存在一份。,存在方法的静态域中
- 随着类的加载而加载,可以通过 "类名.静态方法="的方式进行调用
- 静态方法中,只能调用静态的方法或属性【生命周期是一致的】;非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
在静态的方法内,不能使用this关键字、super关键字【这两个的声明周期在static后面,或者说是基于对象的】
- 对于属性:
- 可以被多个对象所共享且不会随着对象的不同而不同
- 类中的常量也常常声明为static
- 对于方法:
- 操作静态属性的方法,通常设置为static
- 工具类中的方法,习惯上声明为static,如 Math、Arrays、Collection
注: Java内部布局:
- 栈:局部变量
- 堆:new出来的结构——对象、数组
- 方法区:类的加载信息、静态域、常量池
final 可以用来修饰的结构:类、方法、变量
static final: 用来修饰属性:全局常量
abstract 可以用来修饰的结构:类、方法
- 此类不能实例化
- 抽象类中一定有构造器,便于此类对象实例化使用(涉及子类对象实例化的全过程)
- 开发中会提供抽象类的子类,让子类对象实例化,完成相关操作
- 抽象方法只有方法的声明,没有方法体
- 包含抽象方法的类一定是一个抽象类【要保证该方法不能被调用】,反之,抽象类里可以没有抽象方法
- 若子类重写了父类中的所有的抽象方法,此子类方可实例化;若子类没有重写父类中的所有的抽象方法,则子类也是一个抽象类,需要使用abstract修饰
- abstract不能用来修饰:属性、构造器等结构
- abstract不能用来修饰:
- 私有方法【子类不是抽象类的话就必须重写父类的抽象方法,矛盾】
- 静态方法【静态的只有一个不需要重写,子类需要重写抽象方法,矛盾】
- final方法【不让重写,矛盾】
- final类【不让继承,矛盾】
注: 抽象类可以继承非抽象的类,如Object类
//创建了一个匿名子类的对象 p
PersonA p = new PersonA(){
@Override
public void eat() {
System.out.println("匿名子类吃东西");
}
@Override
public void breathe() {
System.out.println("匿名子类呼吸");
}
};
method1(p);
System.out.println("*****************");
//创建匿名子类的匿名对象
method1(new PersonA() {
@Override
public void eat() {
System.out.println("好好吃饭");
}
@Override
public void breathe() {
System.out.println("好好呼吸");
}
});
public static void method1(PersonA p){
p.eat();
p.breathe();
}
Java中,接口和类是并列的结构
如何定义接口: 定义接口中的成员
- JDK7以前:只能定义全局常量和抽象方法
- 全局常量: public static final,但可以省略不写
- 抽象方法:public abstract,但可以省略不写
- JDK8以后:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
- 接口中定义的静态方法,只能通过接口来调用
- 通过实现类的对象,可以调用接口中的默认方法
- 如果实现类重写了接口中的默认方法,则调用时为实现类中重写的方法
- 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么在子类没有重写此方法的情况下,默认调用的是父类中的同名同参的方法——类优先原则
- 如果实现类实现了多个接口,而者多个接口定义了同名同参的默认方法,那么在实现类没有重写此方法的情况下,报错——接口冲突,解决方式为在实现类中重写此方法
- 使用” 接口名.super.方法名"调用接口中的被重写的方法,或者"super.方法名"调用父类中的被重写的方法
注:- 接口中 不能定义构造器,说明接口不可以实例化
- 开发中,接口通过让类去实现(implements) 的方式【
面向接口编程
】来使用:
- 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
- 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
Java类可以实现多个接口,意味着弥补了Java单继承的局限性
格式: class AA extends BB implements CC,DD,EE
接口与接口之间可以继承,而且可以多继承
格式: interface CC extends AA,BB{}
- 接口的具体使用体现多态性
- 接口实质上可以看作是一种规范
面试题:
public class test0 extends B implements A{
public void px(){
System.out.println(x);
}
public static void main(String[] args) {
new test0().px();
}
}
interface A{
int x = 0;
}
class B{
int x = 1;
}
//编译不通过,x模糊
//System.out.println(x);
System.out.println(super.x);//父类的
System.out.println(A.x);//全局变量
//或者,父类和接口中的名字不一样
- 相同点:不能实例化;都可以包含抽象方法;可以被继承
- 不同点:
- 抽象类和接口的定义、内部结构解释说明
- 类:单继承,接口:多继承;接口:多实现
设计模式 是在大量的实践中中介和理论化之后优选的代码结构、编程风格以及解决问题的思考方式【“套路”】
单例设计模式: 采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例 ,并且该类只提供一个取得对象的方法
—— 类的构造器的访问权限设置为private:这样就不能用new操作符在类的外部产生类的对象了,只能在类内部产生该类的对象
——在类内部创建一个静态对象
——提供一个静态方法以返回类内部创建的对象
——指向类内部产生的该类对象的变量也必须定义成静态的
具体步骤:
public class SingletonTest0 {
public static void main(String[] args) {
Bank bank1 = Bank.getInstance();
Bank bank2 = Bank.getInstance();
System.out.println(bank1 == bank2);
}
}
class Bank{
//1.私有化类的构造器
private Bank(){
}
//2.类内部创建类的对象
//4.要求此对象也必须声明为静态的
private static Bank instance = new Bank();
//3.提供公共的静态的方法,返回内部对象
public static Bank getInstance(){
return instance;
}
}
public class SingletonTest1 {
public static void main(String[] args) {
Order o1 = Order.getInstance();
Order o2 = Order.getInstance();
System.out.println(o1 == o2);
}
}
class Order{
//1.私有化类的构造器
private Order(){
}
//2.声明内部类对象
//4.此对象也必须声明为static
private static Order instance = null;//这里就是主要区别
//3.声明公共的静态的返回内部类对象的方法
public static Order getInstance(){
if(instance == null){//没有判断,那不得调一次方法创建一次对象
instance = new Order();
}
return instance;
}
}
一点想法: 是不是可以联想装箱的valueOf,比如Integer的范围-128 ~ +127
区分饿汉式与懒汉式:
- 饿汉式
- 好处:线程安全
- 缺点:对象加载时间过长
- 懒汉式:
- 优点:延迟对象的创建
- 缺点:线程不安全【eg,两个线程同时进入静态方法,同时判断为null -> 同时new】---- > 后续会优化
- main()方法是程序的入口
- main()方法也是一个普通的静态函数
- main()方法可以作为与控制台交互的方式,看下图
当功能内部一部分实现是确定的,一部分实现是不确定的,这时可以把不确定的部分暴露出去,让子类去实现
具体做法:类为抽象类,不确定的方法是抽象方法
实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的
实例化对象这个操作由工厂来做【包装new操作】
注: 设计模型和面向对象设计,原则都是为了使得开发项目更加容易扩展和维护,解决方式就是“分工”
工厂模式的分类:
- 简单工厂模式:用于生产同一等级结构中的任意产品(对于增加新的产品,需要修改已有的代码,不能进行扩展即违反了开闭原则OCP——对扩展开放,对修改关闭)
- 工厂方法模式:用来生产同一等级结构中的固定产品——设置一个工厂接口,再细分为不同产品的产品工厂【不同的实现类】(支持增加任意产品,但还是无法正真避免代码改动,增加新产品时要么再抽象工厂【接口】中增添判断逻辑——像简单工厂一样,要么再重新写一个新产品对应的工厂——这也是写死了)—— 一种解决: Java的反射机制结合配置文件
- 抽象工厂模式:用来生产不同产品族的全部产品(对于增加新的产品,无能为力:支持增加产品族)