Java基础11

单例设计模式

设计模式: 是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。

经典的设计模式一共有23种 

单例模式:就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。

如何实现: ① 饿汉式  ② 懒汉式

优点:由于到单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永远驻留内存的方式来解决。

饿汉式

复制代码
public class BankTest {
    public static void main(String[] args) {
        Bank bank1 = Bank.getInstance();
        Bank bank2 = Bank.getInstance();
    }
}

//饿汉式
class Bank{
    // 1. 类的构造器私有化
    private Bank(){

    }
    // 2.在类的内部创建当前类的实例
    // 4.此属性必须声明为static的
    private static Bank instance = new Bank();
    // 3.使用getXxx()方法获取当前类的实例,必须声明为static的  
//(因为这个方法我们是要返回instance对象,本来是没有这个对象想要获得,所以我们固然不能使用instance.方法的形式来调用,故声明为static的,通过类.方法的实行调用此静态方法)
public static Bank getInstance(){ return instance; //因为声明了static 所以方法内只能用静态的结构,故instance只能静态化 } }
复制代码

懒汉式

复制代码
public class GirlFriend {
    public static void main(String[] args) {
        Girl g1 = Girl.getInstance();
    }
}
// 懒汉式
class Girl{
    // 1.类的构造器私有化
    private Girl(){

    }
    // 2.声明当前类的实例
    // 4.此属性必须声明为static的
    private static Girl instance = null;
    // 3.通过getXxx()方法获取当前类的实例,如果未创建对象,则在方法内部进行创建
    public static Girl getInstance(){
        if (instance == null){
            instance = new Girl();
        }
        return instance;
    }
}
复制代码

对比两种模式

特点:

> 饿汉式: "立即加载",随着类的加载,当前的唯一实例就创建了

> 懒汉式: "延迟加载",在需要使用的时候,进行创建

优缺点:

> 饿汉式: (优点)写法简单,由于内存中较早加载,使用更方便、更快。是线程安全的。 (缺点)内存中占用时间较长

> 懒汉式: (优点)在需要的时候创建,节省内存空间 (缺点)线程不安全(后粗多线程会细讲)

main()方法

public static void main(String[] args) {}

理解1:看做是一个普通的静态方法

理解2:看做是程序的入口,格式是固定的

复制代码
public class MainTest {
    public static void main(String[] args) { //程序的入口
        String[] arr = new String[]{"AA","BB","CC"};
        Main.main(arr);
    }
}

class Main{
    public static void main(String[] args) { //看做是普通的静态方法
        System.out.println("Main的main()的调用");
        for (int i = 0; i < args.length; i ++){
            System.out.println(args[i]);
        }
    }
}
复制代码

与控制台交互

 如何从键盘获取数据:

方式一:使用Scanner

方式二:使用main()的形参进行传值

public class MainTest {
    public static void main(String[] args) { //程序的入口
        for (int i = 0; i <args.length; i++){
            System.out.println("hello" + args[i]);
        }
    }
}

① 借助控制台输入

 ② IDEA

 类的成员之四 代码块

 类中可声明的结构:属性、方法、构造器; 代码块(或初始化块)、内部类

代码块的作用:用来初始化类或对象的信息(即初始化类或对象的成员变量)

代码块只能使用static进行修饰或者不修饰

分类: 静态代码块:使用static修饰  非静态代码块:没有使用static修饰

静态代码块

> 随着类的加载而执行
> 由于类的加载只会执行一次,进而静态代码块的执行,也只会执行一次
> 作用:用来初始化类的信息
> 内部可以声明变量、调用属性或方法、编写输出语句等操作。
> 静态代码块的执行要先于非静态代码块的执行
> 如果声明有多个静态代码块,则按照声明的先后顺序执行
> 静态代码块内部只能调用静态的结构(即静态的属性、方法),不能调用非静态的结构(即非静态的属性、方法)

非静态代码块

> 随着对象的创建而执行
> 每创建当前类的一个实例,就会执行一次非静态代码块
> 作用:用来初始化对象的信息
> 内部可以声明变量、调用属性或方法、编写输出语句等操作。
> 如果声明有多个非静态代码块,则按照声明的先后顺序执行
> 非静态代码块内部可以调用静态的结构(即静态的属性、方法),也可以调用非静态的结构(即非静态的属性、方法)

复制代码
public class BlockTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        System.out.println(p1.age);
    }
}
class Person{
    String name;
    int age;
    static String info = "一个人";

    public void eat(){
        System.out.println("吃饭");
    }
    public Person(){}

    //非静态代码块  随着对象的创建而执行
    {
        System.out.println("非静态代码块");
        age = 1;
    }
    //静态代码块  随着类的加载而执行
    static {
        System.out.println("静态代码块");
        System.out.println("info = " + info);
    }
}
复制代码

 案例

声明User类
包含属性: userName (String类型),password (String类型),registrationTime (long类型) 私有化
包含get/set方法,其中registrationTime没有set方法
-包含无参构造
  - 输出“新用户注册”,
  - registrationTime赋值为当前系统时间
  - userName就默认为当前系统时间值,
  - password默认为“123456"
-包含有参构造(String userName,String password),
  - 输出”新用户注册”,
  - registrationTime赋值为当前系统时间
  - username和password由参数财值
包含public String getInfo()方法,返回:“用户名: xx,密码: xx,注册时间: xx"

复制代码
public class User {
    private String userName;
    private String password;
    private long registrationTime;

    //代码块  把两个构造器中相同的代码放入  对象创建自动执行
    {
        System.out.println("新用户注册");
        registrationTime = System.currentTimeMillis(); //获取系统当前时间  (距离1970-1-1 00:00:00的毫秒数)
    }

    public User() {
        userName = System.currentTimeMillis() +"";
        password = "123456";
    }

    public User(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }

    public String getInfo() {
        return "用户名='" + userName + '\'' +
                ", 密码='" + password + '\'' +
                ", 注册时间=" + registrationTime;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public long getRegistrationTime() {
        return registrationTime;
    }

}
复制代码

非静态的属性的赋值位置

① 默认初始化

② 显示初始化 或  ⑤ 代码块中初始化

③ 构造器中初始化

④ 有对象以后,通过 "对象.方法" 的方法进行赋值

执行的先后顺序:

① - ②/⑤(两个谁在前面谁先)- ③ - ④

(超纲)关于字节码文件中的<init>的简单说明:(通过插件jclasslib bytecode viewer查看)

<init>方法在字节码文件中可以看到。每个<init>方法都对应着一个类的构造器。(类中声明了几个构造器就有几个<init>方法)
编写的代码中的构造器在编译以后就会以<init>方法的方式呈现
<init>方法内部的代码包含了实例变量的显示赋值、代码块中的赋值和构造器中的代码。
<init>方法用来初始化当前创建的对象的信息的。

给实例变量赋值的位置很多,开发中如何选

> 显示赋值:比较适合于每个对象的属性值相同的场景
> 构造器中赋值:比较适合于每个对象的属性值不相同的场景

 练习

复制代码
public class Test1 {
    public static void main(String[] args) {
        new HelloB();
    }
}
class HelloA{
    public HelloA(){
        System.out.println("HelloA");
    }
    {
        System.out.println("I am A class");
    }
    static {
        System.out.println("static A");
    }
}
class HelloB extends HelloA{
    public HelloB(){
        System.out.println("HelloB");
    }
    {
        System.out.println("I am B class");
    }
    static {
        System.out.println("static B");
    }
}
复制代码

问:输出结果如何?

首先要加载父类HelloA,先加载静态代码块,父类的加载完加载子类HelloB的; 随着对象的创建 先执行父类的非静态代码块 然后构造器  最后是子类的非静态代码块 最后子类的构造器

复制代码
public class Test2 {
    static int x;
    static {
        int x = 5;
        x--;
    }
    static {
        x--;
    }

    public static void main(String[] args) {
        System.out.println("x = " + x);
    }
}
复制代码

问: 输出多少?

在第一个static静态代码块中,x = 5 后续的x--实际上调用的是上面的赋值5的x(就近原则),但是到第二个静态代码块,调用的是全局变量x = 0

所以输出 x = -1

复制代码
public class Test3 {
    public static void main(String[] args) {
        Sub s = new Sub();
    }
}
class Base{
    Base(){
        method(100);
    }
    {
        System.out.println("base");
    }
    public void method(int i){
        System.out.println("base:" + i);
    }
}
class Sub extends Base{
    Sub(){
        super.method(70);
    }
    {
        System.out.println("sub");
    }

    @Override
    public void method(int i) {
        System.out.println("sub:" + i);
    }
}
复制代码

问:如何输出?

这里没有静态代码块,所以先调用父类的非静态代码块,然后执行构造器method(100) 但是并不是执行的父类Base中的method方法,执行的是子类Sub中的method方法,因为子类的method已经覆盖了父类(重写了) 所以执行子类的method(100)。

然后执行子类的代码块,最后是子类的构造器调用父类的method方法

 final关键字

理解:最终的

final可以修饰的结构:类、方法、变量

final修饰类

表示此类不能被继承  比如 String、StringBuffer、StringBuilder

 final修饰方法

表示此方法不能被重写,final不能修饰构造器方法(构造器不能被重写不能被继承,所以final修饰也没有意义)

 final修饰变量

既可以修饰成员变量,也可以修饰局部变量。

此时的“变量”其实就变成了“常量”,意味着一旦赋值,就不可更改。

final修饰的是一个基本数据类型的变量,那么这个变量的值就定了,不能变了。

如果修饰的是一个引用变量,那么该变量存的是一个内存地址,该地址就不能变了,但是该内存地址所指向的那个对象还是可以变的。

 地址不能改变但是对象可变

public class StaticTest {
    public static void main(String[] args) {
        final StringBuffer str = new StringBuffer("java区22号");
        // str = new StringBuffer("PHP区33号");
        str.append("\t20区343号");
        System.out.println(str.toString()); //java区22号    20区343号
    }
}

final修饰成员变量

可以给成员变量赋值的位置:

① 显示赋值  ② 代码块中赋值  ③ 构造器中赋值

复制代码
public class FinalTest {
    public static void main(String[] args) {
        A a = new A();
    }
}

class A{
    // 成员变量
    final int MIN_SCORE = 0; //显示赋值
    final int MAX_SCORE;
    final int LEFT;
    { //代码块赋值
        MAX_SCORE = 100;
    }
    public A(){ //构造器赋值
        LEFT = 1;
    }
}
复制代码

final修饰局部变量

一旦赋值,不可更改

> 方法内声明的局部变量: 在调用局部变量之前,一定需要赋值。而且一旦赋值,不可再更改。

> 方法的形参: 在调用此方法时,给形参进行赋值。而且一旦赋值就不可更改。

 final与static搭配:修饰成员变量时,此成员变量称为:全局常量(全字母大写)。

比如:Math的PI

 

posted on   gjwqz  阅读(6)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示