Java面向对象编程----高级部分(上)

本文详细讲解了Java中的静态变量、方法和代码块的概念及其使用。通过示例代码展示了静态变量如何被类的所有实例共享,静态方法如何调用,以及静态代码块在类加载时的执行。此外,还探讨了类变量的访问、静态方法的特性以及单例模式的应用。
摘要由CSDN通过智能技术生成

 

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 + '\'' +
                '}';
    }
}

 

 

总结:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值