java——类变量和类方法;代码块;内部类

一、类变量和类方法

1.1、类变量

1.1.1、类变量内存布局(静态变量放在哪里?)

1、JVM7及以前的近代变量放在方法区中;JVM8以后的静态变量放在堆中

2、不管static变量在哪里,共识:

1)Static变量是同一个类所有对象共享的

2)Static类变量在类加载的时候就生成了

1.1.2、什么是类变量

类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。

1.1.3、如何定义类变量

1、定义语法

访问修饰符 static 数据类型变量名;
static 访问修饰符 数据类型变量名;

2、访问类变量

类名.类变量名
对象名.类变量名

1.1.4、类变量使用注意事项和细节讨论

1、什么时候需要用类变量

当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计所有学生共交多少钱。Student(name, static fee)

2、类变量与实例变量(普通属性)区别

类变量是该类的所有对象共享的,而实例变量是每个对象独享的。

3、加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量

4、类变量的定义不能放在方法里面,因它是属于类的。可以在方法中使用类变量,但不能定义类变量

5、类变量可以通过 “类名.类变量名”或者“对象名.类变量名”来访问,但java设计者推荐我们使用“类名.类变量名”方式访问。【前提是满足访问修饰符的访问权限和范围】

6、实例变量不能通过类名.类变量名方式访问。

7、类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,就可以使用类变量了。

8、.类变量的生命周期是随类的加载开始,随着类消亡而销毁。

public class StaticDetail {
    public static void main(String[] args) {
        B b = new B();
        //System.out.println(B.n1);
        System.out.println(B.n2);
        //静态变量是类加载的时候,就创建了,所以我们没有创建对象实例
        //也可以通过类名.类变量名来访问
        System.out.println(C.address);

    }
}

class B {
    public  int n1 = 100;
    public static int n2 = 200;
}

class C {
    public static String address = "北京";
}

1.2、类方法

1.2.1、类方法基本介绍

1、类方法也叫静态方法。

2、定义语法

访问修饰符 static 数据返回类型方法名 (){}
static 访问修饰符 数据返回类型方法名 (){}

1.2.2、类方法的调用【前提是满足访问修饰符的访问权限和范围】

类名.类方法名
对象名.类方法名

1.2.3、类方法的经典使用场景

1、当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。

2、比如:工具类中的方法 utils;Math类、Arrays类、Collections集合类看下源码

3、小结:在程序员实际开发,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用了,比如打印一维数组,冒泡排序,完成某个计算任务等

1.2.4、类方法使用注意事项和细节讨论

1、类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数;普通方法中隐含着this的参数

2、类方法可以通过类名调用,也可以通过对象名调用。

3、普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用。

4、类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。

5、类方法(静态方法),只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员(必须遵守访问权限)

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(this.n2);不能使用
        hi();//OK
        //say();//错误
    }

    //普通成员方法,既可以访问  非静态成员,也可以访问静态成员
    //小结: 非静态方法可以访问 静态成员和非静态成员
    public void ok() {
        //非静态成员
        System.out.println(n1);
        say();
        //静态成员
        System.out.println(n2);
        hello();

    }
}

二、理解 main 方法语法

2.1、深入理解 main 方法

public static void main(String[] args){}

1、main方法时虚拟机调用
2、java虚拟机需要调用类的main/方法,所以该方法的访问权限必须是public
3、java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
4、该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数

IDEA接收参数:editor Configurations -> program arguments

5、java 执行的程序  参数1  参数2  参数3

2.1、特别提示:

1、在 main()方法中,我们可以直接调用 main 方法所在类的静态方法或静态属性。

2、但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员

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();
    }
}

三、代码块

3.1、代码块的基本介绍

1、代码化块又称为初始化块,属于类中的成员【即是类的一部分】,类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。

2、但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。

3.2、代码块的基本语法

[修饰符]{
代码
};

说明注意:

1、“修饰符”可选,要写的话,也只能写 static

2、代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块/非静态代码块。

3、逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)

4、“;”号可以写上,也可以省略。

3.3、代码块的代码块的好处和使用场景

1、相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作

注:不管调用哪个构造器,创建对象,都会先调用代码块的内容,代码块调用的顺序优先于构造器

2、场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性

3.4、代码块的使用注意事项和细节讨论(重要)

3.4.1、static代码块

也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行。

3.4.2、类什么时候被加载【重要】

①创建对象实例时(new)
②创建子类对象实例,父类也会被加载
③使用类的静态成员时(静态属性,静态方法)

3.4.3、普通的代码块

在创建对象实例时,会被隐式的调用,被创建一次,就会调用一次。如果只是使用类的静态成员时,普通代码块并不会执行。

3.4.4、创建一个对象时,在一个类调用顺序是【重点】

①调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
②调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
调用构造方法

5、构造器的最前面其实隐含了 super()和调用普通代码块,新写一个类演示,静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的

class A {
    public A(){//构造器
        //这里有隐藏的执行要求
        //(1)superO);//这个知识点,在前面讲解继承讲解过
        //(2)调用普通代码块的
        System.out.println("ok");
    }
}
3.4.4.1、示例

我们看一下创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:【面试题】
①父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
②子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
③父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
④父类的构造方法
⑤子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
⑥子类的构造方法

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();//对象
        new C02();
    }
}

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 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();
    }
}

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
    }
}

3.4.5、注:

静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。

四、内部类

4.1、基本介绍

1、一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。

2、是我们类的第五大成员【类的五大成员是哪些?属性、方法、构造器、代码块、内部类】

3、内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系,后面看底层源码时,有大量的内部类

4.2、基本语法

class Outer{//外部类
    class Innerf{//内部类
    
    }
}
class Other{//外部其他类
    
}

4.3、内部类的分类

4.3.1、定义在外部类局部位置上(比如方法内)

1、局部内部类(有类名)

2、匿名内部类(没有类名,重点!!!)

4.3.2、定义在外部类的成员位置上

1、成员内部类(没用static修饰)

2、静态内部类(使用static修饰)

4.4、局部内部类

说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。

1、可以直接访问外部类的所有成员,包含私有的
2、不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final 修饰,因为局部变量也可以使用final
3、作用域:仅仅在定义它的方法或代码块中。
4、局部内部类--访问-->外部类的成员【访问方式:直接访问】
5、外部类--访问-->局部内部类的成员【访问方式:创建对象,再访问(注意:必须在作用域内)】
注意:(1)局部内部类定义在方法中/代码块(2)作用域在方法体或者代码块中(3)本质仍然是一个类
6、外部其他类--不能访问-->局部内部类(因为局部内部类地位是一个局部变量)
7、如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

public class LocalInnerClass {//
    public static void main(String[] args) {
        //演示一遍
        Outer02 outer02 = new Outer02();
        outer02.m1();
        System.out.println("outer02的hashcode=" + outer02);
    }
}

class Outer02 {//外部类
    private int n1 = 100;

    private void m2() {
        System.out.println("Outer02 m2()");
    }//私有方法

    public void m1() {//方法
        //1.局部内部类是定义在外部类的局部位置,通常在方法
        //3.不能添加访问修饰符,但是可以使用final 修饰
        //4.作用域 : 仅仅在定义它的方法或代码块中

        final class Inner02 {//局部内部类(本质仍然是一个类)
            //2.可以直接访问外部类的所有成员,包含私有的
            private int n1 = 800;

            public void f1() {
                //5. 局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2()
                //7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
                //   使用 外部类名.this.成员)去访问
                //   Outer02.this 本质就是外部类的对象, 即哪个对象调用了m1, Outer02.this就是哪个对象
                System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);
                System.out.println("Outer02.this hashcode=" + Outer02.this);
                m2();
            }
        }

        //6. 外部类在方法中,可以创建Inner02对象,然后调用方法即可
        Inner02 inner02 = new Inner02();
        inner02.f1();

    }
}

4.5、匿名内部类(重要)

(1)本质是类(2)内部类(3)该类没有名字(4)同时还是一个对象
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名

4.5.1、匿名内部类的基本语法:

类或接口 对象名 = new  类或接口 (参数列表){
类体
};

对象名.方法 (new 接口(){
类体
},形参列表)

编译类型:等号左边。

运行类型:匿名内部类(系统生成)。

参数列表传给构造器。继承类实现接口

4.5.2、重要:涉及①继承②多态③动态绑定④内部类

1、匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法

2、可以直接访问外部类的所有成员,包含私有的

3、不能添加访问修饰符,因为它的地位就是一个局部变量。

4、作用域:仅仅在定义它的方法或代码块中。

5、匿名内部类--访问-->外部类成员 【访问方式:直接访问】

6、外部其他类--不能访问--->匿名内部类(因为 匿名内部类地位是一个局部变量)

7、如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口
2、匿名内部类中是不能定义构造函数的。
3、匿名内部类中不能存在任何的静态成员变量和静态方法
4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

package com.hspedu.innerclass;

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        Outer05 outer05 = new Outer05();
        outer05.f1();
        //外部其他类---不能访问----->匿名内部类
        System.out.println("main outer05 hashcode=" + outer05);
    }
}

class Outer05 {
    private int n1 = 99;

    public void f1() {
        //创建一个基于类的匿名内部类
        //不能添加访问修饰符,因为它的地位就是一个局部变量
        //作用域 : 仅仅在定义它的方法或代码块中

        Person p = new Person(){
            private int n1 = 88;
            @Override
            public void hi() {
                //可以直接访问外部类的所有成员,包含私有的
                //如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
                //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
                System.out.println("匿名内部类重写了 hi方法 n1=" + n1 +
                        " 外部内的n1=" + Outer05.this.n1 );
                //Outer05.this 就是调用 f1的 对象
                System.out.println("Outer05.this hashcode=" + Outer05.this);
            }
        };

        p.hi();//动态绑定, 运行类型是 Outer05$1

        //也可以直接调用, 匿名内部类本身也是返回对象
        // class 匿名内部类 extends Person {}
//        new Person(){
//            @Override
//            public void hi() {
//                System.out.println("匿名内部类重写了 hi方法,哈哈...");
//            }
//            @Override
//            public void ok(String str) {
//                super.ok(str);
//            }
//        }.ok("jack");

    }
}

class Person {//类
    public void hi() {
        System.out.println("Person hi()");
    }
    public void ok(String str) {
        System.out.println("Person ok() " + str);
    }
}
//抽象类/接口...

*局部内部类和匿名内部类的比较

4.6、成员内部类

说明:成员内部类是定义在外部类的成员位置,并且没有static修饰。

1、可以直接访问外部类的所有成员,包含私有的
2、可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
3、作用域:和外部类的其他成员一样,为整个类体,在外部类的成员方法中创建成员内部类对象,再调用方法。
4、成员内部类--访问-->外部类成员(比如:属性)【访问方式:直接访问】
5、外部类--访问-->成员内部类【访问方式:创建对象,再访问】
6、外部其他类---访问--->成员内部类
7、如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

public class MemberInnerClass01 {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.t1();
        // 外部其他类,使用成员内部类的三种方式
        // 第一种方式
        // outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员
        // 这就是一个语法,不要特别的纠结.
        Outer08.Inner08 inner08 = outer08.new Inner08();
        inner08.say();
        // 第二方式 在外部类中,编写一个方法,可以返回 Inner08对象
        Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
        inner08Instance.say();


    }
}

class Outer08 { //外部类
    private int n1 = 10;
    public String name = "张三";

    private void hi() {
        System.out.println("hi()方法...");
    }

    //1.注意: 成员内部类,是定义在外部内的成员位置上
    //2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
    public class Inner08 {//成员内部类
        private double sal = 99.8;
        private int n1 = 66;

        public void say() {
            //可以直接访问外部类的所有成员,包含私有的
            //如果成员内部类的成员和外部类的成员重名,会遵守就近原则.
            //,可以通过  外部类名.this.属性 来访问外部类的成员
            System.out.println("n1 = " + n1 + " name = " + name + " 外部类的n1=" + Outer08.this.n1);
            hi();
        }
    }

    //方法,返回一个Inner08实例
    public Inner08 getInner08Instance(){
        return new Inner08();
    }

    //写方法
    public void t1() {
        //使用成员内部类
        //创建成员内部类的对象,然后使用相关的方法
        Inner08 inner08 = new Inner08();
        inner08.say();
        System.out.println(inner08.sal);
    }
}

4.7、静态内部类的使用

说明:静态内部类是定义在外部类的成员位置,并且有static修饰
1、可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
2、可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
3、作用域:同其他的成员,为整个类体
4、静态内部类--访问--->外部类(比如:静态属性)【访问方式:直接访问所有静态成员】
5、外部类--访问-->静态内部类【访问方式:创建对象,再访问】
6、外部其他类---访问--->静态内部类

方式1:因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)

Outer.Inner inner = new Outer.Inner();

方式2:编写一个方法(或静态方法),可以返回静态内部类的对象实例.

Outer10 outer10 = new Outer10();Outer10.Inner10 inner101 = outer10.getInner10();
Outer10.Inner10 inner10_ = Outer10.getInner10_();//【无需创建对象】

7、如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问

public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
        outer10.m1();
        //外部其他类 使用静态内部类
        //方式1
        //因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
        Outer10.Inner10 inner10 = new Outer10.Inner10();
        inner10.say();
        //方式2
        //编写一个方法(或静态方法),可以返回静态内部类的对象实例.
        Outer10.Inner10 inner101 = outer10.getInner10();
        System.out.println("============");
        inner101.say();

        Outer10.Inner10 inner10_ = Outer10.getInner10_();
        System.out.println("************");
        inner10_.say();
    }
}

class Outer10 { //外部类
    private int n1 = 10;
    private static String name = "张三";

    private static void cry() {}
    //Inner10就是静态内部类
    //1. 放在外部类的成员位置
    //2. 使用static 修饰
    //3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
    //4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
    //5. 作用域 :同其他的成员,为整个类体
    static class Inner10 {
        private static String name = "韩顺平教育";
        public void say() {
            //如果外部类和静态内部类的成员重名时,静态内部类访问的时,
            //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
            System.out.println(name + " 外部类name= " + Outer10.name);
            cry();
        }
    }

    public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问
        Inner10 inner10 = new Inner10();
        inner10.say();
    }

    public Inner10 getInner10() {
        return new Inner10();
    }

    public static Inner10 getInner10_() {
        return new Inner10();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值