面向对象_高级1

面向对象_高级1

1. 关键字:static

  • 使用范围:

    • 在Java类中,可用static修饰属性、方法、代码块、内部类
  • 被修饰后的成员具备以下特点:

    • 随着类的加载而加载
    • 优先于对象存在
    • 修饰的成员,被所有对象所共享
    • 访问权限允许时,可不创建对象,直接被类调用

1.1 静态变量

  • 语法格式
[修饰符] class{
	[其他修饰符] static 数据类型 变量名;
}
  • 特点
    • 静态变量的默认值规则和实例变量一样。

    • 静态变量值是所有对象共享。

    • 静态变量在本类中,可以在任意方法、代码块、构造器中直接使用。

    • 如果权限修饰符允许,在其他类中可以通过“类名.静态变量”直接访问,也可以通过“对象.静态变量”的方式访问(但是更推荐使用类名.静态变量的方式)。

    • 静态变量的get/set方法也静态的,当局部变量与静态变量重名时,使用“类名.静态变量”进行区分。

1.2 静态方法

  • 语法格式
[修饰符] class{
	[其他修饰符] static 返回值类型 方法名(形参列表){
        方法体
    }
}
  • 特点
    • 静态方法在本类的任意方法、代码块、构造器中都可以直接被调用。
    • 只要权限修饰符允许,静态方法在其他类中可以通过“类名.静态方法“的方式调用。也可以通过”对象.静态方法“的方式调用(但是更推荐使用类名.静态方法的方式)。
    • 在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构。
    • 静态方法可以被子类继承,但不能被子类重写
    • 静态方法的调用都只看编译时类型。
    • 因为不需要实例就可以访问static方法,因此static方法内部不能有this,也不能有super。如果有重名问题,使用“类名.”进行区别。

2. 单例设计模式

  • 单例设计模式(Singleton Design Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来访问该实例。单例模式在需要确保全局唯一性或控制资源的访问时非常有用。下面介绍两种实现方式:
  • 饿汉式
class Singleton {
    // 1.私有化构造器
    private Singleton() {
    }

    // 2.内部提供一个当前类的实例
    // 4.此实例也必须静态化
    private static Singleton single = new Singleton();

    // 3.提供公共的静态的方法,返回当前类的对象
    public static Singleton getInstance() {
        return single;
    }
}
  1. 私有化构造器的目的是防止外部类通过 new 关键字创建 Singleton 类的实例。这样可以确保这个类只能通过内部的静态方法来创建实例。
  2. 在类内部创建一个 Singleton 类的唯一实例,并将其赋值给一个静态变量 single。这个变量是类级别的,不依赖于任何对象实例。当类加载时,就会创建 single 实例,确保只有一个实例存在。
  3. 提供一个公共的静态方法 getInstance(),用于返回 Singleton 类的唯一实例。
  • 懒汉式
class Singleton {
    // 1.私有化构造器
    private Singleton() {
    }
    // 2.内部提供一个当前类的实例
    // 4.此实例也必须静态化
    private static Singleton single;
    // 3.提供公共的静态的方法,返回当前类的对象
    public static Singleton getInstance() {
        if(single == null) {
            single = new Singleton();
        }
        return single;
    }
}
  1. 将构造器设为私有,防止外部类通过 new 关键字直接创建 Singleton 类的实例。这确保了外部无法直接实例化该类,从而保证了单例模式的唯一性。
  2. 声明一个静态变量 single 来持有 Singleton 类的唯一实例。这个变量是 static 的,意味着它是类级别的,不依赖于任何实例。初始时,singlenull,表示尚未创建实例。
  3. 提供一个公共的静态方法 getInstance(),用于获取 Singleton 类的唯一实例。这个方法是类级别的,可以在没有实例的情况下调用。

3. main 方法的语法

​ 在 Java 中,main 方法是程序的入口点。Java 虚拟机(JVM)从 main 方法开始执行程序的代码。理解 main 方法的语法和功能对于编写 Java 程序至关重要。以下是对 main 方法语法的详细解释:

3.1 main 方法的基本语法

public static void main(String[] args) {
    // 程序的代码
}

3.2 语法分析

  1. public:

    • 含义: 访问修饰符 public 表示 main 方法可以被任何类访问。因为 main 方法是程序的入口点,JVM 需要能够从外部调用它,所以必须将其声明为 public
  2. static:

    • 含义: 关键字 static 表示 main 方法是静态的。静态方法属于类而不是类的实例。这意味着 main 方法可以在没有创建类的实例的情况下被调用。JVM 不会先创建类的实例就直接调用 main 方法,因此 main 方法必须是静态的。
  3. void:

    • 含义: void 表示 main 方法没有返回值。main 方法的目的是启动程序,而不是返回数据给调用者。
  4. main:

    • 含义: main 是方法名,JVM 根据这个方法名来识别程序的入口点。main 是一个标准名称,不可以更改。
  5. String[] args:

    • 含义: String[]main 方法的参数类型,表示接收一个字符串数组。这个数组用来传递命令行参数给程序。
    • 使用: 通过命令行运行 Java 程序时,可以传递参数,这些参数会作为字符串数组传递到 main 方法。例如,如果在命令行中运行 java MyClass arg1 arg2,那么 args 数组将包含 "arg1""arg2"

4. 类的成员:代码块

​ 在Java中,类的成员不仅包括字段(成员变量)和方法,还包括代码块(Code Blocks)。代码块分为静态代码块、实例代码块、同步代码块和局部代码块。每种代码块都有特定的用途和执行顺序。下面我们来全面介绍这些代码块:

4.1 静态代码块(Static Initialization Block)

  • 定义: 静态代码块是使用 static 关键字定义的代码块。它在类加载时执行,并且只执行一次,无论创建多少个类的实例。

  • 作用: 用于初始化静态变量,或在类加载时需要执行的逻辑。

  • 执行时机: 静态代码块在类的静态成员(如静态变量和静态方法)被访问前,或者类的第一个对象被创建之前执行。

  • 语法:

    class MyClass {
        static {
            // 静态代码块的内容
            System.out.println("Static block executed");
        }
    }
    
  • 示例:

    class Example {
        static int counter;
        
        static {
            counter = 10;
            System.out.println("Static block executed, counter = " + counter);
        }
        
        public Example() {
            System.out.println("Constructor executed");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Example ex1 = new Example(); // 静态代码块在类加载时执行一次
            Example ex2 = new Example(); // 此时不会再次执行静态代码块
        }
    }
    

4.2 实例代码块(Instance Initialization Block)

  • 定义: 实例代码块不使用 static 关键字,是在类中直接定义的代码块。每次创建对象时,都会执行一次实例代码块。
  • 作用: 用于初始化实例变量,或者需要在构造器之前执行的逻辑。
  • 执行时机: 实例代码块在每次调用构造器之前执行,无论使用哪个构造器。
  • 语法:
    class MyClass {
        {
            // 实例代码块的内容
            System.out.println("Instance block executed");
        }
    }
    
  • 示例:
    class Example {
        int id;
        
        {
            id = 100;
            System.out.println("Instance block executed, id = " + id);
        }
        
        public Example() {
            System.out.println("Constructor executed");
        }
        
        public Example(int id) {
            this.id = id;
            System.out.println("Constructor with parameter executed, id = " + id);
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Example ex1 = new Example();      // 实例代码块和无参构造函数依次执行
            Example ex2 = new Example(200);  // 实例代码块和有参构造函数依次执行
        }
    }
    

4.3 同步代码块(Synchronized Block)

  • 定义: 同步代码块使用 synchronized 关键字定义,确保多线程环境下,某一代码块在同一时间只能被一个线程执行,从而避免线程安全问题。
  • 作用: 保证代码块在多线程环境下的原子性和可见性。
  • 执行时机: 当多个线程尝试访问同步代码块时,只有获得锁的线程才能进入,其他线程必须等待。
  • 语法:
    class MyClass {
        public void myMethod() {
            synchronized (this) {
                // 同步代码块的内容
            }
        }
    }
    
  • 示例:
    class Counter {
        private int count = 0;
        
        public void increment() {
            synchronized (this) {
                count++;
                System.out.println("Count: " + count);
            }
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Counter counter = new Counter();
            
            Runnable task = () -> {
                for (int i = 0; i < 5; i++) {
                    counter.increment();
                }
            };
            
            Thread t1 = new Thread(task);
            Thread t2 = new Thread(task);
            
            t1.start();
            t2.start();
        }
    }
    

4.4 局部代码块(Local Block)

  • 定义: 局部代码块是在方法内部或构造函数内部使用 {} 括起来的代码段,用于限制变量的作用域或控制执行流程。
  • 作用: 在特定范围内定义变量,以节省内存或组织代码逻辑。
  • 执行时机: 局部代码块在方法或构造函数被调用时,按照顺序执行。
  • 语法:
    public void myMethod() {
        {
            int x = 10;
            System.out.println("Local block executed, x = " + x);
        }
        // 这里 x 已经超出了作用域,无法访问
    }
    
  • 示例:
    public class Main {
        public static void main(String[] args) {
            {
                int temp = 5;
                System.out.println("Local block, temp = " + temp);
            }
            // temp 变量在此处已经超出作用域,无法再访问
        }
    }
    

总结

  • 静态代码块: 在类加载时执行,主要用于初始化静态变量或在类加载时需要执行的代码。
  • 实例代码块: 在每次创建对象时执行,用于初始化实例变量或在构造函数之前执行的代码。
  • 同步代码块: 在多线程环境下使用,确保代码块在同一时间只能被一个线程执行,保证线程安全。
  • 局部代码块: 用于限制变量作用域或组织代码逻辑,通常在方法或构造函数内部使用。

举例:分析加载顺序

class Root{
	static{
		System.out.println("Root的静态初始化块");
	}
	{
		System.out.println("Root的普通初始化块");
	}
	public Root(){
		System.out.println("Root的无参数的构造器");
	}
}
class Mid extends Root{
	static{
		System.out.println("Mid的静态初始化块");
	}
	{
		System.out.println("Mid的普通初始化块");
	}
	public Mid(){
		System.out.println("Mid的无参数的构造器");
	}
	public Mid(String msg){
		//通过this调用同一类中重载的构造器
		this();
		System.out.println("Mid的带参数构造器,其参数值:"
			+ msg);
	}
}
class Leaf extends Mid{
	static{
		System.out.println("Leaf的静态初始化块");
	}
	{
		System.out.println("Leaf的普通初始化块");
	}	
	public Leaf(){
		//通过super调用父类中有一个字符串参数的构造器
		super("尚硅谷");
		System.out.println("Leaf的构造器");
	}
}
public class LeafTest{
	public static void main(String[] args){
		new Leaf(); 
		//new Leaf();
	}
}

本例解析:

​ 这个问题涉及到构造器链的细节,以及Java如何确保在初始化子类对象时,父类部分先被正确初始化。

Java构造器的执行顺序

  • 在Java中,当你创建一个对象时,构造器的执行顺序遵循以下规则:
    1. 先调用父类的构造器:在任何子类构造器中,如果没有显式调用 super(...),Java会在子类构造器的第一行自动插入 super(),即调用父类的无参构造器。

    2. 初始化父类部分:在父类的构造器执行完之前,子类的构造器不会执行。

    3. 执行子类的实例初始化块:父类构造器完成后,再执行子类的构造器和实例初始化块。

具体执行流程:我们先看这一部分代码

class Mid extends Root {
    public Mid() {
        System.out.println("Mid的无参数的构造器");
    }

    public Mid(String msg) {
        this();  // 调用当前类的无参数构造器 Mid()
        System.out.println("Mid的带参数构造器,其参数值:" + msg);
    }
}

class Root {
    public Root() {
        System.out.println("Root的无参数的构造器");
    }
}

​ 当执行 new Leaf() 时,Leaf 类的构造器调用了 super("尚硅谷"),这会调用 Mid 的带参数构造器 Mid(String msg)。让我们详细分析这个过程中每一步的执行:

Mid(String msg) 的执行

  • super("尚硅谷") 会调用 Mid(String msg) 构造器。
  • Mid(String msg) 构造器的第一行是 this(),这意味着调用 Mid() 无参构造器。

Mid() 无参构造器的执行

  • this() 调用 Mid() 构造器时(这一步非常关键!!!
    • 根据Java的构造器调用规则,在 Mid() 构造器执行之前,必须先完成对其父类 Root 的初始化。
    • 因此,Java会自动在 Mid() 的构造器的最前面插入一个隐式的 super(),去调用 Root 的无参构造器。

Root() 构造器的执行

  • Root() 构造器执行,输出 Root的无参数的构造器
  • Root 类的构造器执行完毕后,控制权返回到 Mid() 无参构造器中。

Mid() 无参构造器的执行

  • Root 类的构造器完成后,Mid() 构造器继续执行,输出 Mid的无参数的构造器

回到 Mid(String msg) 构造器

  • 完成 Mid() 构造器的执行后,控制权返回到 Mid(String msg) 构造器继续执行,输出 Mid的带参数构造器,其参数值:尚硅谷

总结:

​ 在 this() 调用 Mid() 无参构造器时,Java需要确保父类 Root 部分已经被初始化。因此,首先会隐式调用 Root() 构造器。只有在 Root 构造器完成后,Mid() 构造器才会执行,这也是为什么在 this() 调用后,先执行 Root 构造器,而不是立即执行 Mid 的无参构造器。

​ 这个过程确保了在任何对象创建时,父类部分的初始化总是在子类部分之前完成,从而保证了整个对象的正确构造和初始化。

  • 17
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值