代码块
基本介绍
- 代码块又称为初始化块,属于类的一部分。
- 和方法不同的是,没有方法名、返回值和参数,只有方法体,而且通过加载类或创建对象时隐式调用,不用显示调用。
- 相当于另一种形式的构造,可以做初始化的操作。当多个构造器中需要用到重复的语句,可以将这些语句抽取到初始化块中,简洁代码。
- 不管调用哪个构造器,都会先调用代码块。
基本语法
[修饰符]{
代码内容
}
注:
-
修饰符可选,但只有static可选
-
有static修饰的叫做静态代码块,没有的叫做普通代码块
-
大括号里可以写任何逻辑语句(输入、输出、调用、循环、判断)
-
;
号可以写也可以省略,看个人习惯
//CodeBlock01.java
public class CodeBlock01 {
public static void main(String[] args) {
//主函数中分别调用不同的构造器
Movie movie1 = new Movie("1");
Movie movie2 = new Movie("2",100);
Movie movie3 = new Movie("3",200,"张三");
}
}
class Movie{
private String name;
private double price;
private String director;
//假设有三个重载的构造器,都要输出一个开场信息
//可以将这个开场信息放在代码块中,这样构造器会直接调用
{
System.out.println("电影屏幕打开...");
System.out.println("广告开始");
System.out.println("电影正式开始");
}
public Movie(String name) {
System.out.println("调用了构造器1\n");
this.name = name;
}
public Movie(String name, double price) {
System.out.println("调用了构造器2\n");
this.name = name;
this.price = price;
}
public Movie(String name, double price, String director) {
System.out.println("调用了构造器3\n");
this.name = name;
this.price = price;
this.director = director;
}
}
输出结果
电影屏幕打开...
广告开始
电影正式开始
调用了构造器1
电影屏幕打开...
广告开始
电影正式开始
调用了构造器2
电影屏幕打开...
广告开始
电影正式开始
调用了构造器3
注意事项
-
static 代码块作用就是对类进行初始化,随着类的加载而执行,且只会执行一次。但如果是普通代码块,每创建一个对象就执行一次。
-
类什么时候被加载:
- 创建对象实例时
- 创建子类对象实例,父类也会被调用一次。父类先加载。
- 使用类的静态成员时
-
普通代码块在创建对象实例时被隐式调用,被创建一次就会调用一次。
-
如果只使用类的静态成员,普通代码块并不会被执行
//CodeBlockDetail.java
//以下最好分开运行查看效果(把另一点创建实例注释掉)。
public class CodeBlockDetail01 {
public static void main(String[] args) {
//被加载的三种情况
//1. 创建对象实例时被加载
AA a = new AA();
//2. 创建子类对象实例,父类也会被加载一次
BB bb = new BB();
//3. 使用类的静态成员时也会被加载
System.out.println(CC.n1);
//普通代码块和静态代码块被调用的情况
DD dd = new DD();
DD dd1 = new DD();
//直接调用静态成员而不创建对象
System.out.println(DD.n1);
}
}
class AA{
//静态代码块
static {
System.out.println("AA 的静态代码块被执行");
}
}
class BB extends AA{
static {
System.out.println("BB 的静态代码块被执行");
}
}
class CC {
public static int n1 = 999;//静态属性
static {
System.out.println("CC 的静态代码块被执行");
}
}
class DD{
public static int n1 = 8888;//静态属性
static {//静态代码块
System.out.println("DD的静态代码块被执行");
}
{//普通代码块只有创建对象时才会调用
System.out.println("DD的普通代码块被执行");
}
}
输出结果:
// AA a = new AA();
AA 的静态代码块被执行
// BB bb = new BB();
AA 的静态代码块被执行
BB 的静态代码块被执行
//同时运行 AA a = new AA();BB bb = new BB();
//因为每个类只会加载一次,不把第1点注释掉而同时运行第1、2点的话,由于BB继承于AA,在调用第2点时AA已经加载过了,只会输出2条结果
AA 的静态代码块被执行
BB 的静态代码块被执行
// System.out.println(CC.n1);
CC 的静态代码块被执行
999
// DD dd = new DD(); DD dd1 = new DD();
//静态代码块只被调用一次,普通代码块每创建一次就被调用一次
DD的静态代码块被执行
DD的普通代码块被执行
DD的普通代码块被执行
// System.out.println(DD.n1);
// DD中的普通代码块未被调用
DD的静态代码块被执行
8888
- 创建对象时在一个类内调用的优先级是:
静态 > 普通 > 构造器
注:静态代码块和静态属性之间优先级相同,有多个则按照定义顺序调用。普通代码块和普通属性也是如此。
下面这个例子中,如果只定义了变量(没有=号),则不会调用getN1或者getN2方法。但这里在类内初始化了,因此一定会调用getN1或getN2。
//CodeBlockDetail02.java
public class CodeBlockDetail02 {
public static void main(String[] args) {
A a = new A();
}
}
class A{
private static int n1 = getN1();
private int n2 = getN2();
static {//静态代码块
System.out.println("静态代码块");
}
public static int getN1(){//静态方法
System.out.println("静态方法getN1被调用");
return 100;
}
{//普通代码块
System.out.println("普通代码块");
}
public int getN2(){//普通方法
System.out.println("普通方法getN2被调用");
return 200;
}
public A(){//无参构造器
System.out.println("A()的无参构造器");
}
}
输出结果(被调用的顺序)
静态方法getN1被调用
静态代码块
普通方法getN2被调用
普通代码块
A()的无参构造器
-
构造器隐含的内容:
- super()
- 调用普通代码块的机制
这些内容即使未显式声明也会调用,且类之间拥有继承关系时,调用优先级如下:
父类静态 > 子类静态 > 父类普通 > 父类构造器 > 子类普通 > 子类构造器
其中“静态”包含静态代码块和静态属性,“普通”包含普通代码块和普通属性
//CodeBlockDetail03.java
public class CodeBlockDetail03 {
public static void main(String[] args) {
BBB bbb = new BBB();
}
}
class AAA{
{
System.out.println("AAA的普通代码块");
}
static {
System.out.println("AAA的静态代码块");
}
public AAA(){
//super();
//调用本类普通代码块
System.out.println("AAA的构造器");
}
}
class BBB extends AAA{
{
System.out.println("BBB的普通代码块");
}
static {
System.out.println("BBB的静态代码块");
}
public BBB() {
//super();
//调用本类普通代码块
System.out.println("BBB的构造器");
}
}
输出结果
AAA的静态代码块
BBB的静态代码块
AAA的普通代码块
AAA的构造器
BBB的普通代码块
BBB的构造器
- 静态代码块只能调用静态成员,普通代码块可以调用任意成员
单例设计模式
设计模式
设计模式是在大量的实践中总结和理论化之后优选的代码结构、变成风格以及解决问题的思考方式,免得我们自己再思考和摸索。
单例模式是什么
- 采取一定的方法保证在整个的软件系统重对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
- 两种形式:饿汉式和懒汉式
步骤
- 构造器私有化:防止用户之间创建对象
- 类内创建对象
- 向外提供一个静态的公共方法,如
getInstance
说明
-
为什么叫饿汉式:未使用就已经在类内初始化好了一个对象(显得很着急)
-
为什么叫懒汉式:你不用就不创建,避免资源浪费
-
两种设计模式区别:
-
饿汉式在类加载时就创建了对象实例,懒汉式在使用时才创建
-
饿汉式不存在线程安全问题,懒汉式则存在
-
饿汉式可能浪费资源。因为程序员有可能并不创建对象实例。
-
-
javaSE
标准类中的java.lang.Runtime
就是一个经典的单例模式
//singleTon01.java
public class singleTon01 {
public static void main(String[] args) {
girlFriend instance = girlFriend.getInstance();
System.out.println(instance);
}
}
class girlFriend{
//单例模式-饿汉式:
private String name;
//1. 构造器私有化:防止用户之间创建对象
private girlFriend(String name){
this.name = name;
}
//2. 类内创建对象
//为了能接收静态方法返回的参数,也需要用static修饰
private static girlFriend gf = new girlFriend("小红");
//3. 向外提供一个静态的公共方法,如`getInstance
//为了不创建实例就能调用,需要用static修饰
public static girlFriend getInstance(){
return gf;
}
@Override
public String toString() {
return "girlFriend{" +
"name='" + name + '\'' +
'}';
}
}
输出结果:
girlFriend{name='小红'}
//singleTon2.java
public class singleTon02 {
public static void main(String[] args) {
//第一次,未存在,开始创建
Cat instance = Cat.getInstance();
System.out.println(instance);
//第二次,已存在,开始返回
Cat instance2 = Cat.getInstance();
System.out.println(instance2);
}
}
class Cat{
//单例模式-懒汉式
private String name;
public static int n1 = 999;
//1. 构造器私有化
private Cat(String name){
System.out.println("构造器调用");
this.name = name;
}
//2. 定义一个static 静态对象
private static Cat cat; //默认值null
//3. 提供一个public static方法返回Cat对象
// 没有才新建,否则直接返回
public static Cat getInstance(){
if (cat == null) {
cat = new Cat("小可爱");
}
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
输出结果
构造器调用
Cat{name='小可爱'}
Cat{name='小可爱'}