我发现还有其他哥们和我一起在跟着韩老师学习,然后笔记也大部分重复,导致原创没通过,就先改成转载的好了,那哥们真是比我勤快多了,1个月前就学到这了
代码块
是什么
代码化块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过 {} 包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
基本语法
[修饰符]{
代码
}; //可以没有; //我觉得还是有比较好,这样比较容易和其他的区分开来
说明注意:
1) 修饰符可选, 要写的话只能写static
2) 代码块分为两类, 使用static修饰的叫静态代码块, 没有static修饰的, 叫普通代码块/非静态代码块。
3)逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
4);号可以写上,也可以省略。
什么情况用
- 相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
- 场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
细节(重点)
- static代码块也叫静态代码块, 作用就是对类进行初始化, 而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行一次。
- 类什么时候被加载**[重要背!]**
- 创建对象实例时(new)
- 创建子类对象实例,父类也会被加载
- 使用类的静态成员时(静态属性,静态方法)
- 普通的代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。
如果只是使用类的静态成员时,普通代码块并不会执行。
小结:
1.static代码块是类加载时, 执行, 且只会执行一次
2.普通代码块是在创建对象时调用的,创建一次,调用一次
3.类加载的3种情况,需要记住.
public class CodeBlock01 {
public static void main(String[] args) {
// Person person = new Person();
// Person person1 = new Person();
// Person person2 = new Person();静态代码块只调用一次
System.out.println(Person.n1);//普通的代码块没调用,静态的调用了
}
}
class Person{
static{
System.out.println("静态代码块调用");
};
{
System.out.println("普通代码块调用");
};
static int n1 = 100;
}
-
创建一个对象时,在一个类调用顺序:(重点,难点)
- 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
- 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
- 调用构造方法
-
构造器的最前面其实隐含了super() 和调用普通代码块, 静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的
class A{
public A() { //构造器
//这里有隐藏的执行要求
//(1) super() ; //这个知识点, 在前面讲解继承的时候, 老师说
//(2)调用普通代码块的
System.out.print In("ok") ;
}
}
- 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。
代码块执行顺序
创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
①父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
②子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
③父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
④父类的构造方法
⑤子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
⑥子类的构造方法
单例设计模式
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索。
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
单例模式有两种方式:1)饿汉式2)懒汉式
//1)饿汉式
//构造器私有化=》防止直接new
//类的内部创建对象
//向外暴露一个静态的公共方法
public class Single01 {
public static void main(String[] args){
Cat cat = Cat.getInstance();
}
}
class Cat{
private String name;
private static Cat cat = new Cat("哈哈");
private Cat(String name) {
System.out.println("有参构造器被调用..");
this.name = name;
}
public static Cat getInstance(){
return cat;
}
}
//2)懒汉式懒汉式
//构造器私有化=》防止直接new
//类的内部创建对象
//向外暴露一个静态的公共方法
public class Single02 {
public static void main(String[] args){
Dog dog = Dog.getInstance();
Dog dog1 = Dog.getInstance();
System.out.println(dog == dog1);
}
}
class Dog{
private String name;
private static Dog dog;
private Dog(String name) {
System.out.println("有参构造器被调用..");
this.name = name;
}
public static Dog getInstance(){
if(dog == null){
dog = new Dog("阿巴阿巴");
}
return dog;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
'}';
}
}
饿汉式和懒汉式 区别
- 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题
- 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题
final
final可以修饰类、属性、方法和局部变量。
final class Test{ } //类
final int TAX_RATE = 111;//属性
public final void hah(){ } //方法
在某些情况下, 程序员可能有以下需求, 就会使用到final:
- 当不希望类被继承时, 可以用final修饰
- 当不希望父类的某个方法被子类覆盖/重写(override) 时, 可以用final关键字修饰
- 当不希望类的的某个属性的值被修改, 可以用final修饰
- 当不希望某个局部变量被修改, 可以使用final修饰
细节
- final修饰的属性又叫常量, 一般用XX_XX_XX来命名
- final修饰的属性在定义时, 必须赋初值, 并且以后不能再修改, 赋值可以在如下位置之一【选择一个位置赋初值即可】:
//定义时
public final double TAX_RATE=0.08
//在构造器中
class Test{
final double TAX_RATE;
public Test() {
TAX_RATE = 0.8;
}
}
//在代码块中
class Test{
final double TAX_RATE;
{
TAX_RATE = 0.8;
}
}
- 如果final修饰的属性是静态的, 则初始化的位置只能是
- 定义时
- 在静态代码块不能在构造器中赋值
- 定义时
final class Test{
static final double TAX_RATE;
static{
TAX_RATE = 0.8;
}
} //可行
----------------------------------------------------------
final class Test{
static final double TAX_RATE;
{
TAX_RATE = 0.8; //报错
}
} // XXXXXXXXXXXXX ---不行
- final类不能继承, 但是可以实例化对象
- 如果类不是final类, 但是含有final方法, 则该方法虽然不能重写, 但是可以被继承
- 一般来说, 如果一个类已经是final类了, 就没有必要再将方法修饰成final方法
- final不能修饰构造方法(即构造器)
- final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理
public class FinalTest {
public static void main(String[] args){
System.out.println(Test.aa);//10 类没有加载
}
}
class Test{
static final int aa = 10;
static{
System.out.println("Test类被加载了");
}
}
- 包装类(Integer, Double, Float, Boolean等都是final) , String也是final类
抽象类
当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类。
//用abstract关键字来修饰一个类时, 这个类就叫抽象类
访问修饰符 abstract class 类名{
//用abstract关键字来修饰一个方法时, 这个方法就是抽象方法
访问修饰符 abstract void 方法名(); //没有方法体 {}
//有返回值的方法
public abstract double hh(double hh);
}
//抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类)
细节
- 抽象类不能被实例化
- 抽象类不一定要包含abstract方法。也就是说, 抽象类可以没有abstract方法
- 一旦类包含了abstract方法, 则这个类必须声明为abstract
- abstract只能修饰类和方法, 不能修饰属性和其它的
- 抽象类可以有任意成员【抽象类本质还是类】,比如:非抽象方法、构造器、静态属性等等
- 抽象方法不能有主体(方法体),即不能实现
- 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类
- 抽象方法不能使用 private、final 和 static 来修饰,因为这些关键字都是和重写相违背的
模版设计模式
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
- 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
- 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式.
//实例 - 模版设计模式
public abstract class Template { //抽象类父类
public abstract void job(); //抽象方法
public void calculateTime(){ //普通方法
long start = System.currentTimeMillis();
//统一的部分写进去
//System.currentTimeMillis() 统计1970年至今的毫秒数
job(); //不确定的交由子类重写方法
long end = System.currentTimeMillis();
System.out.println("任务工作时间" + (end-start));
}
}
// AA子类
public class AA extends Template{ // 继承抽象父类
@Override
public void job() { //重写父类抽象方法
long sum = 0;
for (long i = 1; i < 1000000; i++) {
sum += i;
}
}
}
// BB子类
public class BB extends Template{ // 继承抽象父类
@Override
public void job() { //重写父类抽象方法
long sum = 0;
for (long i = 1; i < 1000000; i++) {
sum *= i;
}
}
}