Java-Day-12( 类变量 + 类方法 + main 方法 + 代码块 + 单例设计模式 + final 关键字 )
Posted 朱呀朱-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java-Day-12( 类变量 + 类方法 + main 方法 + 代码块 + 单例设计模式 + final 关键字 )相关的知识,希望对你有一定的参考价值。
Java-Day-12
类变量
-
定义一个变量 count,是一个类变量 ( 静态变量 ) static
class Person private String name; // 该静态变量 static 最大的特点就是会被 Person 所有的对象实例共享 public static int count = 0; public Person(String name) this.name = name; // main 中 Person p1 = new Person("zhu"); Person p2 = new Person("zhang"); System.out.println("静态变量可在 main 中直接用类调用:" + Person.count);
-
jvm 中静态变量的存储
- 如先前所讲,Person 加载时会在方法区加载类信息,栈中的 p1,p2 都指向堆里独立的空间地址,String 类型的变量放进方法区的常量池里
- 静态 static 在堆里有一个新的空间放 count ( 后面讲到的 class 里 ),两个真正的对象都有一个空间指向着这个同在堆里的 count ,被 p1、p2 共享
- 类信息加载时,在方法区有一个空间叫静态域,JDK 7 和 7 之前版本是把 count 放在了静态域里,而不是堆中
- JDK 8 和 8 之后至今是放在了堆里,在堆里会根据反射机制增加一个 class 对象 ( 类加载的时候就会产生,每个类都会产生 ),静态变量就放在 Person 类的 class 对象的最后尾部。
- 但不管版本如何,共识一致:
- static 变量是同一个类所有对象共享的
- static 变量是在类加载的时候就生成了
-
定义语法
- 访问修饰符 static 数据类型 变量名;( 推荐 )
- static 访问修饰符 数据类型 变量名;
-
访问方式
-
类名.类变量名 ( 推荐 )
-
对象名.类变量名
-
注意:
- 因为类变量是随着类的加载而创建的,所以即使没有创建对象实例也可以直接访问
- 类变量的访问也要遵守访问权限 ( private 只能本类访问 )
public static void main(String[] args) System.out.println(Person.count); // 也是可以输出初始化值 0 的
-
-
注意细节
- 当我们需要让某个类的所有对象都共享一个变量时
- 类变量是该类所有对象共享的,而实例变量是每个对象独享的
- 加上 static 就叫做静态变量或者类变量,否则可能会被称为实例变量 / 普通属性 / 普通成员变量 / 非静态属性 / 非静态成员变量
- 推荐在满足访问权限规则的基础上使用 类名.类变量名 的表达方式
- 实例变量不能通过 类名.实例变量 直接访问
- 类变量是在类加载的时候就初始化了,即就算没有创建对象,只要类加载了就可以使用类变量了
- 类变量的生命周期是随着类加载而开始,随着类的消亡而销毁
类方法
-
类方法也叫静态方法
-
定义语法
- 访问修饰符 static 数据返回类型 方法名() ( 推荐 )
- static 访问修饰符 数据返回类型 方法名()
-
调用方式
- 类名.类方法名
- 对象名.类方法名
- 仍要注意修饰符访问权限范围
-
使用场景
- 当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率
- 例如:Math 类 ( Math.sqrt() )、Arrays 类、Collections 集合类
- 如果我们希望不创建实例也可以调用某个方法,即当作工具来使用,就用来当作静态方法
- 实际开发时,往往将一些通用的方法设计成静态方法,就像打印一组数组、冒泡排序、完成某个计算任务等
- 当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率
-
使用细节
-
类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区
- 类方法中无 this 参数 ( 也是类名.类变量等表示方法 )
- 普通方法中隐含 this 的参数
-
普通方法和对象有关,需要通过对象名调用,不能用类名来调用
-
类方法中不允许使用和对象有关的关键字 ( 如:this、super )
-
类方法中只能访问与类变量或者类方法,即静态方法只能访问静态成员
public class T private static String duang; private String name; public void f1() ... public static void f2() ... public static void test() System.out.println(duang); System.out.println(T.duang); // System.out.println(this.duang); // 报错 // System.out.println(name); // 报错 // f1(); // 报错 f2();
-
普通成员方法,既可以访问非静态成员也可以访问静态成员
- 从而就可以通过普通方法对静态变量加以修改
-
main 方法
-
形式:
- public static void main(String[] args)
-
main 方法是虚拟机调用
- java 虚拟机需要调用类的 main() 方法,所以该方法的访问权限必须是 public
- java 虚拟机在执行 main() 方法时不必创建对象,所有该方法必须是 static
- 该方法接收 String 类型的数组参数,该数组中保存执行 java 命令时传递给所运行的类的参数
-
案例演示
// test.java: public class test public static void main(String[] args) // args 是如何传入的 // 遍历显示 for (int i = 0; i < args.length; i++) System.out.println("第" + (i+1) + "个参数=" + args[i]);
-
在命令行里传递参数
- 此时用 DOS 编译,"javac test.java",运行时,"java test n1 n2 n3",就会输出:
第1个参数=n1
第2个参数=n2
第3个参数=n3 - 是在执行程序时,把 "n1 n2 n3" 当作字符串数组 args 传入
- 此时用 DOS 编译,"javac test.java",运行时,"java test n1 n2 n3",就会输出:
-
在 idea 里面传递参数
-
Apply OK 后再运行 test.java 就会输出:
第1个参数=你好呀
第2个参数=Java
第3个参数=朱呀朱
第4个参数=在线
第5个参数=为您
第6个参数=服务
-
-
注意细节
- 在 main() 方法中,我们可以直接调用 main 方法所在类的静态方法或静态属性
- 但是不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
代码块
( codeblock )
-
代码化块又称初始化块,属于类中的成员,即是类的一部分,类似于方法,将逻辑语句封装在方法体中,通过 包围起来
- 但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
-
基本语法
- 修饰符 代码 ;
- 注意
- 修饰符可不写,但要写的话也只能写 static
- 代码块分为两类,使用 static 修饰的叫静态代码块,没有 static 修饰的叫普通代码块 / 非静态代码块
- 逻辑语句可以为任何逻辑语句 ( 输入、输出、方法调用、循环、判断等 )
- ;号也可以写上,也可以省略
-
代码块好处
- 相当于是另外一种形式的构造器 ( 对构造器的补充机制 ),可以做初始化的操作
- 如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
-
实例:
-
下面三个重载构造器都有相同的语句,出现冗余
-
这时就把相同语句放进一个代码块中即可
-
这样的话,不管调用哪个构造器,创建对象,都会先调用代码块的内容,再调用所需构造器
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("买完票了"); // System.out.println("买完吃喝了"); // System.out.println("开始看电影了"); this.name = name; public Movie(String name, double price) // System.out.println("买完票了"); // System.out.println("买完吃喝了"); // System.out.println("开始看电影了"); this.name = name; this.price = price; public Movie(String name, double price, String director) // System.out.println("买完票了"); // System.out.println("买完吃喝了"); // System.out.println("开始看电影了"); this.name = name; this.price = price; this.director = director;
-
-
注意细节
-
static 代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,而且只会执行一次
- 如果是普通代码块,每创建一个对象就执行
-
记住类什么时候被加载
- 创建对象实例时 ( new )
- 创建子类对象实例,或调用子类静态成员时,父类也会被加载
- 使用类的静态成员时 ( 静态属性,静态方法 )
-
普通代码块在创建对象实例时,会被隐式的调用,被创建一次,就调用一次
- 而如果只是使用类的静态成员时,普通代码块并不会执行
- static 代码块是只有类加载时才执行一次
class AA public static String a = "AA的静态属性"; static System.out.println("AA的静态代码块"); System.out.println("AA的普通代码块"); class BB public static String a = "BB的静态属性"; static System.out.println("BB的静态代码块"); System.out.println("BB的普通代码块"); public class test public static void main(String[] args) System.out.println(AA.a); System.out.println("——————————"); AA aa = new AA(); System.out.println("——————————"); BB bb = new BB();
-
输出为:
AA的静态代码块
AA的静态属性
——————————
AA的普通代码块
——————————
BB的静态代码块
BB的普通代码块
-
创建一个对象时,在一个类的调用顺序 ( 优先级 1 ~ 3 )
-
调用静态代码块和静态属性初始化
静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按照他们定义的顺序调用
- 静态属性在静态代码块前面时
public class test public static void main(String[] args) AA aa = new AA(); class AA public static int a = f1(); static System.out.println("静态代码块"); public static int f1() System.out.println("方法f1"); return 1; // 输出为: // 方法f1 // 静态代码块 // main里是System.out.println(AA.a); 的话输出为: // 方法f1 // 静态代码块 // 1
- 静态代码块放静态属性前面时
class AA static System.out.println("静态代码块"); public static int a = f1(); public static int f1() System.out.println("方法f1"); return 1; // 输出为: // 静态代码块 // 方法f1 // main里是System.out.println(AA.a); 的话输出为: // 静态代码块 // 方法f1 // 1
-
调用普通代码块和普通属性的初始化
普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用 ( 似 static,但不管顺序如何输出都是在所有静态的后面 )
-
调用构造方法
-
-
构造器的最前面其实隐含了 super() 和调用普通代码块,而静态相关的代码块,属性初始化,在类加载时,就执行完毕了,所以静态是优于普通代码块和构造器先执行的
class AA public AA() //(1)super() //(2)调用本类的普通代码块 System.out.println("最后轮到构造器")
-
创建一个子类时 ( 继承关系 ),静态代码块、静态属性初始化、普通代码块、普通属性初始化、构造方法的调用顺序如下
- 父类的静态代码块和静态属性 ( 优先级一样,按定义顺序执行 )
- 子类的静态代码块和静态属性 ( 优先级一样,按定义顺序执行 )
- 父类的普通代码块和普通属性初始化 ( 优先级一样,按定义顺序执行 )
- 父类的构造方法
- 子类的普通代码块和普通属性初始化 ( — 普通属性初始化会造成某方法运行输出语句的话 ) ( 优先级一样,按定义顺序执行 )
- 子类的构造方法
// 主main public class test public static void main(String[] args) new BB(); // 父类 class AA private static int a1 = getVal_a1(); static System.out.println("AA静态代码块"); // 2 System.out.println("AA普通代码块"); // 5 public int a2 = getVal_a2(); public static int getVal_a1() System.out.println("getVal_a1"); // 1 return 1; public int getVal_a2() System.out.println("getVal_a2"); // 6 return 1; public AA() System.out.println("AA构造器"); // 7 // 子类 class BB extends AA private static int b1 = getVal_b1(); static System.out.println("BB静态代码块"); // 4 public int b2 = getVal_b2(); System.out.println("BB普通代码块"); // 9 public static int getVal_b1() System.out.println("getVal_b1"); // 3 return 1; public int getVal_b2() System.out.println("getVal_b2"); // 8 return 1; public BB() System.out.println("BB构造器"); // 10
-
静态代码块只能调用静态成员,普通代码块可以调用任意成员
-
单例设计模式
-
设计模式 ( 23 种 )
- 静态方法和属性的经典使用
- 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式
- 设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们再思考和摸索
-
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
-
单例模式
-
饿汉式
-
构造器私有化 ( 防止直接 new )
-
类是内部创建对象
-
向外暴露一个静态的公共方法
-
代码实现
-
实际在 main 里,但凡是涉及了加载了类 Wife ( 就算不调用实例的 get 方法 ),不管有没有用到都创建好了一个实例对象 ( 正式项目时对象一般都比较大,产生无用对象 ),所以叫饿汉
public class test public static void main(String[] args) // 4. 实现 Wife instance = Wife.getInstance(); System.out.println(instance); class Wife private String name; // 2. 类内部创建对象,为了能被静态方法获取,要 static 修饰 private static Wife w = new Wife("静静"); // 1. 为保证只能创建一个 Wife 对象 —— 私有化 private Wife(String name) this.name = name; // 3. static 后,才能在不 new 创建的时候就能调用此方法 public static Wife getInstance() return w; // 没有下述代码,4.输出的就是一个地址 // 加载类Wife,静态属性w获取new堆中对象地址,构造器得name为静静,getInstance()静态方法直接调用,返回w(堆中对象地址)给了类引用名instance,输出此地址 @Override public String toString() return "Wife" + "name=\'" + name + \'\\\'\' + \'\';
-
-
懒汉式
- 构造器私有化
- 定义一个 static 静态属性对象
- 提供一个 public 的 static 方法,可以返回一个 GirlFriend 对象
- 就算是类加载了,也不会调用构造器,即不会创建对象
- 懒汉式,只有当对象调用了实例的 get 方法才能完成对象第一次的创建返回 girlfriend 对象,之后就算再调用也只会返回上次创建的对象
public class test public static void main(String[] args) GirlFriend instance = GirlFriend.getInstance(); System.out.println(instance); class GirlFriend private String name; private static GirlFriend girlfriend; private GirlFriend(String name) this.name = name; // 通过判断对象是否为null来决定是否进行第一次的创建 public static GirlFriend getInstance() if (girlfriend == null) girlfriend = new GirlFriend("静静"); return girlfriend; @Override public String toString() return "Wife" + "name=\'" + name + \'\\\'\' + \'\';
-
饿汉式和懒汉式的区别:
- 二者最主要的区别在于创建对象的时机不同,饿汉式类加载就创建了对象实例,而懒汉式式在使用时才创建
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题 ( 例如在判断对象是否为 null 时若是同时进来多个线程就会都通过到 new,只不过只返回最后一个创建的对象而已 ),在学习线程时再加完善
- 饿汉式存在资源浪费的问题,而懒汉式不存在
-
final 关键字
-
final 可以修饰类、属性、方法和局部变量
-
final 需求场景
-
当不希望类被继承时
final class A
-
当不希望父类的某个方法被子类覆盖 / 重写时
class A public final void f1()
-
当不希望类的某个属性的值被修改
class A public final double TAX_RATE = 0.08;
-
当不希望某个局部变量被修改
class A public void f1() final double TAX_RATE = 0.08; // TAX_RATE = 0.1; 就不能改了
-
-
使用细节
-
final 修饰的属性又叫常量,一般 XX_XX_XX 命名
-
final 修饰的属性在定义时必须赋值,而且以后不能再修改,赋值可以在如下位置之一:
-
定义时
class A public final double TAX_RATE = 0.08;
-
在构造器中
class A public final double TAX_RATE; public A() TAX_RATE = 0.08;
-
在代码块中
class A public final double TAX_RATE; TAX_RATE = 0.08;
-
-
但如果 final 修饰的属性是静态的,则初始化的位置只能是
-
定义时
class A public static final double TAX_RATE = 0.08; // final 写 static 前面后面都可
-
在静态代码块 ( 不能在构造器中赋值 —— 静态在加载时就得创建好 )
class A public final double TAX_RATE; static TAX_RATE = 0.08;
-
-
final 类不能继承,但是可以实例化对象
final class A // main里: A a = new A();
-
如果类不是 final 类,但是含有 final 方法,则该方法虽然不能重写,但是可以被继承使用
class A public final void f1() System.out.println("f1方法"); class B extends A // main里: new B().f1();
-
一般来说,如果一个类已经是 final 类了,就没有必要再将方法修饰成 final 方法 ( 因为类 final 了就没法继承了,更别说重写方法了 )
-
final 不能修饰构造方法 ( 即构造器 )
-
final 和 static 往往搭配使用效率更高,底层编译器做了优化处理,不会导致类加载,只想调用一个静态属性 ( 调用静态属性时不触动静态代码块 )
public class test public static void main(String[] args) System.out.println(A.num); System.out.println(B.num); class A public static int numA = 10; static System.out.println("A静态代码块被执行了"); class B public final static int numB = 10; static System.out.println("B静态代码块被执行了"); // 输出: // A静态代码块被执行了 // 10 // 10
-
包装类 ( Integer,Double,Float,Boolean 等都是 final ),String 也是 final 类,都不能被继承 ( 可 ctrl + B 查看 )
-
-
小练习
-
编写一个程序能够计算圆形的面积,要求圆周率为 3.14
class Circle private double radius; // F1: private final double PI = 3.14; public Circle(double radius) this.radius = radius; // F2: // PI = 3.14; // F3: // PI = 3.14; public double calArea() return PI * radius * radius;
-
判断代码是否有误
public class A public int add(final int x) // 此处可以加final修饰 ++x; // 报错,不准修改 return x + 1; // 正确
-
成员变量实例变量类变量成员方法实例方法类方法的区别
简单来说:
类体的定义包括成员变量的定义和方法的定义。
1、成员变量包括实例变量和类变量;而成员方法包括实例方法、类方法,当然还有一种特
殊的构造方法。
2、类变量、类方法就是类中的变量、方法,必须是静态的,要加static;故其又称静态变量
、静态方法。
3、成员变量、成员方法是对象或实例中的变量、方法,不加static;
类变量:静态域,静态字段,或叫静态变量,它属于该类所有实例共有的属性,在内存中只
有一个地方存储这个变量。而且所有的实例都可以修改这个类变量的值(前提是这个类变量没
有被final修饰,否则是常量了),而且访问类变量的时候不用实例,直接用类就可以了。
类方法:和类变量一样,可以不用实例,直接用类就可以调用类方法。
实例变量:实例域,实例字段,或叫成员变量。
实例方法:或叫成员方法,必须先有实例,然后才能通过实例调用该实例方法。
使用方法:类方法可以直接调用类变量和类方法
类方法不可以直接调用实例变量和实例方法
类方法中没有this,因为没有实例,this不知道调用哪个实例
类方法可以从类里面直接访问类成员
实例方法可以调用类方法,访问类变量,但是不提倡这样做,会把类方法和类变
量混淆成实例方法和实例变量
以上是关于Java-Day-12( 类变量 + 类方法 + main 方法 + 代码块 + 单例设计模式 + final 关键字 )的主要内容,如果未能解决你的问题,请参考以下文章