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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具