package com.hspedu.static_; public class ChildGame { public static void main(String[] args) { //定义一个变量 count, 统计有多少小孩加入了游戏 int count = 0; Child child1 = new Child("白骨精"); child1.join(); //count++; child1.count++; Child child2 = new Child("狐狸精"); child2.join(); //count++; child2.count++; Child child3 = new Child("老鼠精"); child3.join(); //count++; child3.count++; //=========== //类变量,可以通过类名来访问 System.out.println("共有" + Child.count + " 小孩加入了游戏..."); //下面的代码输出什么? System.out.println("child1.count=" + child1.count);//3 System.out.println("child2.count=" + child2.count);//3 System.out.println("child3.count=" + child3.count);//3 } } class Child { //类 private String name; //定义一个变量 count ,是一个类变量(静态变量) static 静态 //该变量最大的特点就是会被Child 类的所有的对象实例共享 public static int count = 0; public Child(String name) { this.name = name; } public void join() { System.out.println(name + " 加入了游戏.."); } }
类加载时,count被加载,count在堆空间里
还有的老师讲 ,类加载时,类信息放在方法区,方法区里面有一个静态域。
不管怎么样:
1.静态变量被对象共享
2.因此不会影响对静态变量的使用
JDK7/8之前放在方法区的
JDK8之后放在堆里面的,通过反射机制会加载一个class对象。
当类加载时,在堆里面生成一个本类(child)的class对象,生成的是原型对象。
package com.hspedu.static_; public class VisitStatic { public static void main(String[] args) { //类名.类变量名 //说明:类变量是随着类的加载而创建,所以即使没有创建对象实例也可以访问 System.out.println(A.name); A a = new A(); //通过对象名.类变量名 System.out.println("a.name=" + a.name); } } class A { //类变量 //类变量的访问,必须遵守 相关的访问权限. public static String name = "韩顺平教育"; //普通属性/普通成员变量/非静态属性/非静态成员变量/实例变量 private int num = 10; }
类名.类变量名(A.name)
说明:类变量是随着类的加载而创建,所以即使没有创建对象实例也可以访问也可以
通过对象名.类变量名
多个名字的,是因为多个人从国外翻译的不一样。
推荐使用类名.类变量名
package com.hspedu.static_; public class StaticMethod { public static void main(String[] args) { //创建2个学生对象,叫学费 Stu tom = new Stu("tom"); //tom.payFee(100); Stu.payFee(100);//对不对?对 Stu mary = new Stu("mary"); //mary.payFee(200); Stu.payFee(200);//对 //输出当前收到的总学费 Stu.showFee();//300 //如果我们希望不创建实例,也可以调用某个方法(即当做工具来使用) //这时,把方法做成静态方法时非常合适 System.out.println("9开平方的结果是=" + Math.sqrt(9)); System.out.println(MyTools.calSum(10, 30)); } } //开发自己的工具类时,可以将方法做成静态的,方便调用 class MyTools { //求出两个数的和 public static double calSum(double n1, double n2) { return n1 + n2; } //可以写出很多这样的工具方法... } class Stu { private String name;//普通成员 //定义一个静态变量,来累积学生的学费 private static double fee = 0; public Stu(String name) { this.name = name; } //说明 //1. 当方法使用了static修饰后,该方法就是静态方法 //2. 静态方法就可以访问静态属性/变量 public static void payFee(double fee) { Stu.fee += fee;//累积到 } public static void showFee() { System.out.println("总学费有:" + Stu.fee); } }
package com.hspedu.static_; public class StaticMethodDetail { public static void main(String[] args) { D.hi();//ok //非静态方法,不能通过类名调用 //D.say();, 错误,需要先创建对象,再调用 new D().say();//可以 } } class D { private int n1 = 100; private static int n2 = 200; public void say() {//非静态方法,普通方法 } public static void hi() {//静态方法,类方法 //类方法中不允许使用和对象有关的关键字, //比如this和super。普通方法(成员方法)可以。 //System.out.println(this.n1); } //类方法(静态方法)中 只能访问 静态变量 或静态方法 //口诀:静态方法只能访问静态成员. public static void hello() { System.out.println(n2); System.out.println(D.n2); // System.out.println(n1);不能使用 //System.out.println(this.n2);不能使用 hi();//OK //say();//错误 } //普通成员方法,既可以访问 非静态成员,也可以访问静态成员 //小结: 非静态方法可以访问 静态成员和非静态成员 public void ok() { //非静态成员 System.out.println(n1); say(); //静态成员 System.out.println(n2); hello(); } }
package com.hspedu.static_; public class StaticExercise02 { } class Person { //StaticExercise02.java 2min 时间 private int id; private static int total = 0; public static int getTotalPerson() { //id++;//错误, 注销 return total; } public Person() {//构造器 total++; //total = 1 id = total;//id = 1 } } class TestPerson { public static void main(String[] args) { System.out.println("Number of total is " +Person.getTotalPerson()); //0 Person p1 = new Person(); System.out.println( "Number of total is "+ Person.getTotalPerson()); //1 } }
package com.hspedu.static_; public class StaticExercise03 { } class Person { //StaticExercise03.java 2min 看 private int id; private static int total = 0; public static void setTotalPerson(int total){ // this.total = total;//错误,因为在static方法中,不可以使用this 关键字 Person.total = total; } public Person() {//构造器 total++; id = total; } //编写一个方法,输出total的值 public static void m() { System.out.println("total的值=" + total); } } class TestPerson { public static void main(String[] args) { Person.setTotalPerson(3); new Person(); //最后 total的值就是4 Person.m();//看看输出的是不是4 ,是4 } }
开始total等于3,后来走构造方法加一
void:返回值是虚拟机接受的,但它不需要返回值,对他没有用,所以不接受,用void。
package com.hspedu.main_; public class Main01 { //静态的变量/属性 private static String name = "韩顺平教育"; //非静态的变量/属性 private int n1 = 10000; //静态方法 public static void hi() { System.out.println("Main01的 hi方法"); } //非静态方法 public void cry() { System.out.println("Main01的 cry方法"); } public static void main(String[] args) { //可以直接使用 name //1. 静态方法main 可以访问本类的静态成员 System.out.println("name=" + name); hi(); //2. 静态方法main 不可以访问本类的非静态成员 //System.out.println("n1=" + n1);//错误 //cry(); //3. 静态方法main 要访问本类的非静态成员,需要先创建对象 , 再调用即可 Main01 main01 = new Main01(); System.out.println(main01.n1);//ok main01.cry(); } }
怎么在Idea下面传递参数
package com.hspedu.main_; public class Main02 { public static void main(String[] args) { for ( int i = 0; i < args.length; i++ ) { System.out.println("args[" + i + "] = " + args[i]); } } }
然后运行:
1.当我们创建对象,都会先调用普通代码块的内容(静态代码块不一样,后面讲),再调用构造器 2.代码块调用的顺序优先于构造器..
package com.hspedu.codeblock_; public class CodeBlock01 { public static void main(String[] args) { Movie movie = new Movie("战狼"); System.out.println("==============="); Movie movie2 = new Movie("唐探3", 100, "陈思诚"); } } class Movie { private String name; private double price; private String director; //3个构造器-》重载 //解读 //(1) 下面的三个构造器都有相同的语句 //(2) 这样代码看起来比较冗余 //(3) 这时我们可以把相同的语句,放入到一个代码块中,即可 //(4) 这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容 //(5) 代码块调用的顺序优先于构造器.. { System.out.println("电影屏幕打开..."); System.out.println("广告开始..."); System.out.println("电影正是开始..."); }; public Movie(String name) { // System.out.println("电影屏幕打开..."); // System.out.println("广告开始..."); // System.out.println("电影正是开始..."); System.out.println("Movie(String name) 被调用..."); 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("电影正是开始..."); System.out.println("Movie(String name, double price, String director) 被调用..."); this.name = name; this.price = price; this.director = director; } }
继承的本质: 先加载父类,在加载子类
类被加载的时候 , 静态代码块这个时候执行
继承的本质: 先加载父类,在加载子类
如果有继承关系,父类的代码块也要加载,并且是先加载。
重点:static代码块是类加载时执行,只会执行一次
总结:
静态代码块的执行和类有没有加载有关系。
普通代码块的执行和你有没有new对象有关,
只有new对象了,构造方法才会执行。
然后普通代码块和构造方法相似,是构造方法的补充。
普通代码块是否执行和类有没有加载没有任何关系
package com.hspedu.codeblock_; public class CodeBlockDetail01 { public static void main(String[] args) { //类被加载的情况举例 //1. 创建对象实例时(new) // AA aa = new AA(); //2. 创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载 // AA aa2 = new AA(); //3. 使用类的静态成员时(静态属性,静态方法) // System.out.println(Cat.n1); //static代码块,是在类加载时,执行的,而且只会执行一次. // DD dd = new DD(); // DD dd1 = new DD(); //普通的代码块,在创建对象实例时,会被隐式的调用。 // 被创建一次,就会调用一次。 // 如果只是使用类的静态成员时,普通代码块并不会执行 System.out.println(DD.n1);//8888, 静态模块块一定会执行 } } class DD { public static int n1 = 8888;//静态属性 //静态代码块 static { System.out.println("DD 的静态代码1被执行...");// } //普通代码块, 在new 对象时,被调用,而且是每创建一个对象,就调用一次 //可以这样简单的,理解 普通代码块是构造器的补充 { System.out.println("DD 的普通代码块..."); } } class Animal { //静态代码块 static { System.out.println("Animal 的静态代码1被执行...");// } } class Cat extends Animal { public static int n1 = 999;//静态属性 //静态代码块 static { System.out.println("Cat 的静态代码1被执行...");// } } class BB { //静态代码块 static { System.out.println("BB 的静态代码1被执行...");//1 } } class AA extends BB { //静态代码块 static { System.out.println("AA 的静态代码1被执行...");//2 } }
class DD { public static int n1 = 8888;//静态属性 //静态代码块 static { System.out.println("DD 的静态代码1被执行..."); } //普通代码块, 在new 对象时,被调用,而且是每创建一个对象,就调用一次 //可以这样简单的,理解 普通代码块是构造器的补充,和类加载无关 { System.out.println("DD 的普通代码块..."); } }
System.out.println(DD.n1);
输出:8888
DD 的静态代码1被执行...
如果只是使用类的静态成员时,
普通代码块并不会执行,静态模块块一定会执行,
因为普通代码块, 在new 对象时,被调用,
而且是每创建一个对象,就调用一次,
这里没有创建对象,不会调用普通代码块普通的代码块:
在创建对象实例时(构造器会被调用),普通的代码块会被隐式的调用。
被创建一次,就会调用一次。普通代码块是构造器的补充,
如果构造器被调用,普通代码块就被调用
如果构造器不被调用,普通代码块就不被调用
先了解静态属性的初始化:
package com.hspedu.codeblock_; public class CodeBlockDetail02 { public static void main(String[] args) { A a = new A();// (1) A 静态代码块01 (2) getN1被调用...(3)A 普通代码块01(4)getN2被调用...(5)A() 构造器被调用 } } class A { { //普通代码块 System.out.println("A 普通代码块01"); } private int n2 = getN2();//普通属性的初始化 static { //静态代码块 System.out.println("A 静态代码块01"); } //静态属性的初始化 private static int n1 = getN1(); public static int getN1() { System.out.println("getN1被调用..."); return 100; } public int getN2() { //普通方法/非静态方法 System.out.println("getN2被调用..."); return 200; } //无参构造器 public A() { System.out.println("A() 构造器被调用"); } }
静态代码块和静态属性的初始化都有,按他们的定义顺序执行
普通代码块和普通属性的初始化都有,按他们的定义顺序执行
package com.hspedu.codeblock_; public class CodeBlockDetail03 { public static void main(String[] args) { new BBB(); //(1)AAA的普通代码块(2)AAA() 构造器被调用(3)BBB的普通代码块(4)BBB() 构造器被调用 } } class AAA { //父类Object { System.out.println("AAA的普通代码块"); } public AAA() { //(1)super() //(2)调用本类的普通代码块 System.out.println("AAA() 构造器被调用...."); } } class BBB extends AAA { { System.out.println("BBB的普通代码块..."); } public BBB() { //(1)super() //(2)调用本类的普通代码块 System.out.println("BBB() 构造器被调用...."); } }
package com.hspedu.codeblock_; public class CodeBlockDetail04 { public static void main(String[] args) { //老师说明 //(1) 进行类的加载 //1.1 先加载 父类 A02 1.2 再加载 B02 //(2) 创建对象 //2.1 从子类的构造器开始 new B02();//对象 } } class A02 { //父类 private static int n1 = getVal01(); static { System.out.println("A02的一个静态代码块..");//(2) } { System.out.println("A02的第一个普通代码块..");//(5) } public int n3 = getVal02();//普通属性的初始化 public static int getVal01() { System.out.println("getVal01");//(1) return 10; } public int getVal02() { System.out.println("getVal02");//(6) return 10; } public A02() {//构造器 //隐藏 //super() //普通代码和普通属性的初始化...... System.out.println("A02的构造器");//(7) } } class B02 extends A02 { // private static int n3 = getVal03(); static { System.out.println("B02的一个静态代码块..");//(4) } public int n5 = getVal04(); { System.out.println("B02的第一个普通代码块..");//(9) } public static int getVal03() { System.out.println("getVal03");//(3) return 10; } public int getVal04() { System.out.println("getVal04");//(8) return 10; } //一定要慢慢的去品.. public B02() {//构造器 //隐藏了 //super() //普通代码块和普通属性的初始化... System.out.println("B02的构造器");//(10) // TODO Auto-generated constructor stub } }
public class CodeBlockDetail04 { public static void main(String[] args) { new C02(); } } class C02 { private int n1 = 100; private static int n2 = 200; private void m1() { } private static void m2() { } static { //静态代码块,只能调用静态成员 //System.out.println(n1);错误 System.out.println(n2);//ok //m1();//错误 m2(); } { //普通代码块,可以使用任意成员 System.out.println(n1); System.out.println(n2);//ok m1(); m2(); } }
代码块练习题:
package com.hspedu.codeblock_; public class CodeBlockExercise01 { } class Person { public static int total;//静态变量 static {//静态代码块 total = 100; System.out.println("in static block!");//输出一次 } } class Test { public static void main(String[] args) { System.out.println("total = "+ Person.total); //100 System.out.println("total = "+ Person.total); //100 } }
类加载会调回静态代码块
静态代码块只输出一次
package com.hspedu.codeblock_; public class CodeBlockExercise02 { } class Sample { Sample(String s) { System.out.println(s); //2 } Sample() { System.out.println("Sample默认构造函数被调用"); //1 } } class Test{ Sample sam1=new Sample("sam1成员初始化");// static Sample sam=new Sample("静态成员sam初始化 ");// static{ System.out.println("static块执行");//1 if(sam==null)System.out.println("sam is null"); // } Test()//构造器 { System.out.println("Test默认构造函数被调用");//3 } //主方法 public static void main(String str[]) { Test a=new Test();//无参构造器 } }
因为没有继承关系,所以Simple类的代码块不会执行
单例模式饿汉式和懒汉式
餓漢式可能造成創建了對象,但是沒有使用.package com.hspedu.single_; public class SingleTon01 { public static void main(String[] args) { // GirlFriend xh = new GirlFriend("小红"); // GirlFriend xb = new GirlFriend("小白"); //通过方法可以获取对象 GirlFriend instance = GirlFriend.getInstance(); System.out.println(instance); GirlFriend instance2 = GirlFriend.getInstance(); System.out.println(instance2); System.out.println(instance == instance2);//T //System.out.println(GirlFriend.n1); //... } } //有一个类, GirlFriend //只能有一个女朋友 class GirlFriend { private String name; //public static int n1 = 100; //为了能够在静态方法中,返回 gf对象,需要将其修饰为static //對象,通常是重量級的對象, 餓漢式可能造成創建了對象,但是沒有使用. private static GirlFriend gf = new GirlFriend("小红红"); //如何保障我们只能创建一个 GirlFriend 对象 //步骤[单例模式-饿汉式] //1. 将构造器私有化 //2. 在类的内部直接创建对象(该对象是static) //3. 提供一个公共的static方法,返回 gf对象 private GirlFriend(String name) { System.out.println("構造器被調用."); this.name = name; } public static GirlFriend getInstance() { return gf; } @Override public String toString() { return "GirlFriend{" + "name='" + name + '\'' + '}'; } }
懶漢式,只有當用戶使用getInstance時,才返回cat對象, 後面再次調用時,會返回上次創建的cat對象package com.hspedu.single_; /** * 演示懶漢式的單例模式 */ public class SingleTon02 { public static void main(String[] args) { //new Cat("大黃"); //System.out.println(Cat.n1); Cat instance = Cat.getInstance(); System.out.println(instance); //再次調用getInstance Cat instance2 = Cat.getInstance(); System.out.println(instance2); System.out.println(instance == instance2);//T } } //希望在程序運行過程中,只能創建一個Cat對象 //使用單例模式 class Cat { private String name; public static int n1 = 999; private static Cat cat ; //默認是null //步驟 //1.仍然構造器私有化 //2.定義一個static靜態屬性對象 //3.提供一個public的static方法,可以返回一個Cat對象 //4.懶漢式,只有當用戶使用getInstance時,才返回cat對象, 後面再次調用時,會返回上次創建的cat對象 // 從而保證了單例 private Cat(String name) { System.out.println("構造器調用..."); this.name = name; } public static Cat getInstance() { if(cat == null) {//如果還沒有創建cat對象 cat = new Cat("小可愛"); } return cat; } @Override public String toString() { return "Cat{" + "name='" + name + '\'' + '}'; } }
总结: