Java内部类的概念,代码块的概念,static关键字

Java内部类的概念,代码块的概念,static关键字,第1张

内部类:

内部类推荐文章:http://t.csdn.cn/Yo8fM

什么是内部类?

(1)定义:内部类( Inner Class )就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类。

为什么要使用内部类?

(2)作用:内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类

内部类的方法可以直接访问外部类的所有数据,包括私有的数据 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便

内部类的分类:

(3)内部类分为:

        成员内部类

        静态内部类

        方法内部类

        匿名内部类(本篇文章不讲解,之后讲解)

内部类之成员内部类
//外部类External
public class External {
    private int a=99;//外部类的私有属性
    //内部类Inner
    public class Inner{
        int b=2;//内部类的成员属性
        //内部类的方法
        public void test(){
            System.out.println("访问外部类中的:a:  "+a);
            System.out.println("访问内部类中的:b:  "+b);
        }
    }
}
class Test04{
    public static void main(String[] args) {
        //创建外部类对象
        External external = new External();
        //创建内部类对象
        External.Inner inner = external.new Inner();
        inner.test();
    }
}

结果:访问内外部类中的:a:  99
      访问内部类中的:b:  2

(1) Inner 类定义在 External类的内部,相当于External 类的一个成员变量的位置,Inner 类可以使用任意访问控制符,如 public 、 protected 、 private 等

(2) Inner 类中定义的 test() 方法可以直接访问External 类中的数据,而不受访问控制符的影响,如直接访问 External 类中的私有属性a。
 

(3) 定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,即:内部类 对象名 = 外部类对象.new 内部类( );

(4) 编译上面的程序后,会发现产生了两个 .class 文件 ,其中,第二个是外部类的 .class 文件,第一个是内部类的 .class 文件,即成员内部类的 .class 文件。总是这样:外部类名$内部类名.class

(5)外部类是不能直接调用内部类的变量和方法的,可先创建内部类的对象,然后通过内部类的对象来访问其成员变量和方法。

(6)如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,如果要访问外部类的成员变量,可以使用 this 关键字。如:

第二条补充:

    注意:内部类访问外部类的变量必须声明为final
      方法中的局部变量,方法结束后这个变量就要释放掉,final保证这个变量始终指向一个对象。
   首先,内部类和外部类其实是处于同一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随者被销毁。问题就来了,如果外部类的方法中的变量不定义final,那么当外部类方法执行完毕的时候,这个局部变量肯定也就被GC了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中,这样的话,由于final所修饰的值始终无法改变,所以这个变量所指向的内存区域就不会变。
     注意,若使用JDK1.8,方法中内部类的方法是可以直接访问外部类的方法的局部变量,并且不需要声明为final类型。

第六条示例代码:

public class External {
    private int b=99;//外部类的私有属性
    //内部类Inner
    public class Inner{
        int b=2;//内部类的成员属性
        //内部类的方法
        public void test(){
            System.out.println("访问外部类中的:b:  "+External.this.b);
            System.out.println("访问内部类中的:b:  "+b);
        }
    }
}
静态内部类

定义:被static修饰的内部类成为静态内部类

使用方法:

(1)静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问

(2)如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员(这里和成员内部类是不同的,成员内部类是通过类名.this.变量名来调用的);如果外部类的静态成员与内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员

(3) 创建静态内部类的对象时,不需要外部类的对象,可以直接创建

内部类 对象名= new 内部类(); 

对比成员内部类:

成员内部类必须通过外部类来创建对象(内部类 对象名 = 外部类对象.new 内部类( );)

示例代码:

//外部类External
public class External {
    private int b=99;//外部类的私有属性
    //内部类Inner
    public static class Inner{
        int b=2;//内部类的成员属性
        //内部类的方法
        public void test(){
            System.out.println("访问外部类中的:b:  "+new External().b);
            System.out.println("访问内部类中的:b:  "+b);
        }
    }
}
class Test04{
    public static void main(String[] args) {

        //创建内部类对象
        External.Inner inner = new External.Inner();
        inner.test();
    }
}
结果是:访问外部类中的:b:  99
        访问内部类中的:b:  2

方法内部类:

定义:方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。

使用注意事项:

(1)因为是方法内部类不能被除了方法之外所使用,所以不能直接mo.print().应该是mo.show().因为这个内部类是定义在show这个方法中的。

(2)由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和 static 修饰符。

好像无法访问外部类方法中的同名属性;

代码示例

//外部类External
public class External {
  //外部类的方法
    public void show(){
        final int a=25;//常量
        int b=13;//变量
        class Inner{
            int c=2;//内部类中的变量
            public void print(){
                System.out.println("访问外部类中的常量a:"+a);
                System.out.println("访问外部类中的变量b:"+b);
                System.out.println("访问内部类中的变量c:"+c);
            }
        }
        Inner inner = new Inner();
        inner.print();//调用内部类的方法
    }
}
class Test04{
    public static void main(String[] args) {
        External external = new External();
        external.show();
    }
}
结果:访问外部类中的常量a:25
      访问外部类中的变量b:13
      访问内部类中的变量c:2

总结:

(1)只有是成员内部类创建内部类对象时候才需要通过外部类对象来创建。其他都是可以直接创建的。

(2)当外部类和内部类变量重名的话,想要调用外部变量。成员内部类需要(类名.this.变量名)。静态内部类需要是(类名.变量名)

Java代码块:

参考文章(比较全面 ):http://t.csdn.cn/y1pB8

什么 是代码块?

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

但是代码块没有方法名,没有返回,没有参数, 只有方法体,而且不用通过对象或类显示调用,而是加载类的时候,或创建对象的时候隐式调用

代码块的分类:

根据代码块定义的位置以及关键字,可分为以下四种:

(1)普通代码块  :普通代码块就是定义在方法中的代码块,也叫本地代码块

(2)构造块  :  定义在类中的代码块(不加修饰符),又称为实例代码块。

                作用:一般用于初始化实例成员变量。

(3)静态块 :使用static定义的代码块称为静态块,也叫静态代码块。

                作用:一般用于初始化静态成员变量。        

                静态代码块内部也可以初始化实例成员变量,但一般不这么做。         

(4)同步代码块(下面没写)

1,代码块调用的顺序优先于构造器的,所以,代码块相当于另外一种形式的构造器,可以作为构造器的补充机制,做初始化的 *** 作。

2, 创建一个对象时,在一个类中的调用顺序:
1) 调用静态代码块和静态属性的初始化(静态代码块和静态属性的初始化调用的优先级一样,如果有多个静态代码块和多个静态属性初始化,则按照他们的定义顺序调用)
2) 调用
实例代码块和普通属性的初始化(实例代码块和普通属性的初始化调用的优先级一样,如果有多个实例代码块和多个普通属性初始化,则按照他们定义的顺序调用)
3)最后调用构造方法

 

注意:

1, 实例代码块,在创建对象实例时,会被隐式调用,被创建一次,就会被调动一次。
可以理解为在用类的构造器实例化对象的时候,构造器中有自动调用普通代码块机制。如果只是使用类的静态成员时,实例代码块并不会执行。

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

3,(构造器中有隐含的super()和调用本类的普通代码块)有继承关系时,他们的 静态代码块,静态属性的初始化 ,普通属性的初始化 ,实例代码块, 构造方法的调用顺序如下:
① 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
② 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
③ 父类的实例代码块和普通属性 (优先级一样,按定义顺序执行)
④ 父类的构造方法
⑤ 子类的实例代码块和普通属性 (优先级一样,按定义顺序执行)
⑥ 子类的构造方法

类加载时机:

1.创建实例对象时(new)
2.创建子类对象实例时,父类也会被加载
3.使用类的静态成员时(静态属性、静态方法)

代码示例:

      1, 实例代码块,在创建对象实例时,会被隐式调用,被创建一次,就会被调动一次。
可以理解为在用类的构造器实例化对象的时候,构造器中有自动调用实例代码块机制。如果只是使用类的静态成员时,普通代码块并不会执行。

public class Test02 {
    public static void main(String[] args) {
        A a1= new A();
        A a2= new A();
        A a3= new A();
    }
}
class A{
    public A(){
        System.out.println("A类的无参构造器"+" ");
    }
    {
        System.out.print("A类中的实例代码块 "+" ");
    }
    static {
        System.out.print("A类中的静态代码块"+" ");
    }
}
结果:A类中的静态代码块 A类中的实例代码块  A类的无参构造器 
      A类中的实例代码块  A类的无参构造器 
      A类中的实例代码块  A类的无参构造器 

注意:如果只是使用类的静态成员时,普通代码块并不会执行(因为没有创建对象)。

public class Test02 {
    public static void main(String[] args) {
        int s = A.S;
        System.out.println(s);
    }
}
class A{
    public static int S=10;
    {
        System.out.print("A类中的实例代码块 "+" ");
    }
    static {
        System.out.print("A类中的静态代码块"+" ");
    }
}
结果:A类中的静态代码块 10

2,创建一个对象时,在一个类中的调用顺序

1, 调用静态代码块和静态属性的初始化(静态代码块和静态属性的初始化调用的优先级一样,如果有多个静态代码块和多个静态属性初始化,则按照他们的定义顺序调用)
2, 调用
实例代码块和普通属性的初始化(实例代码块和普通属性的初始化调用的优先级一样,如果有多个实例代码块和多个普通属性初始化,则按照他们定义的顺序调用)
3,最后调用构造方法

public class Test02 {
    public static void main(String[] args) {
        A a = new A();
    }
}
class A{
    private int num1=getA1();
    public static int num2=getA2();
    public A(){
        System.out.println("A类的无参构造器"+" ");
    }
    {
        System.out.print("A类中的实例代码块 "+" ");
    }
    static {
        System.out.print("A类中的静态代码块"+" ");
    }
    public int getA1(){
        System.out.println("getA1()被执行......");
        return 1;
    }
    public static int getA2(){
        System.out.print("getA2()被执行......"+" ");
        return 2;
    }
}
结果:getA2()被执行...... A类中的静态代码块 getA1()被执行......
      A类中的实例代码块  A类的无参构造器 

3,构造器中有隐含的super()和调用本类的普通代码块

public class Test03 {
    public static void main(String[] args) {
        C c = new C();
    }
}
class B{
    public B(){
        //隐藏的执行
        //1,super().
        //2,调用普通代码块
        System.out.println("父类B的无参构造器");
    }
    {
        System.out.println("父类B中的实例代码块");
    }
}
class C extends B{
    public C(){
        //隐藏的执行
        //1,super().
        //2,调用实例代码块
        System.out.println("子类C的无参构造器");
    }
    {
        System.out.println("子类C中的普通代码块");
    }
}
结果:父类B中的实例代码块
    父类B的无参构造器
    子类C中的实例代码块
    子类C的无参构造器

4,有继承关系时,他们的 静态代码块,静态属性的初始化 ,普通属性的初始化 ,实例代码块, 构造方法的调用顺序如下:
① 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
② 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
③ 父类的实例代码块和普通属性 (优先级一样,按定义顺序执行)
④ 父类的构造方法
⑤ 子类的实例代码块和普通属性 (优先级一样,按定义顺序执行)
⑥ 子类的构造方法

public class Test03 {
    public static void main(String[] args) {
        C c = new C();
    }
}
class B{
    private static int num1=getB1();
    private int num2=getB2();

    public B(){
        //隐藏的执行
        //1,super().
        //2,调用普通代码块
        System.out.println("父类B的无参构造器");
    }
    {
        System.out.println("父类B中的实例代码块");
    }
    static {
        System.out.println("父类B中的静态代码块");
    }
    public static int getB1(){
        System.out.println("父类B的静态属性初始化");
        return 1;
    }
    private int getB2() {
        System.out.println("父类B的普通属性初始化");
        return 2;
    }


}
class C extends B{
   private static int num3=getC1();
   private int num4=getC2();
    public C(){
        //隐藏的执行
        //1,super().
        //2,调用普通代码块
        System.out.println("子类C的无参构造器");
    }
    {
        System.out.println("子类C中的实例代码块");
    }
    static {
        System.out.println("子类C中的静态代码块");
    }
    public static int getC1(){
        System.out.println("子类C的静态属性初始化");
        return 3;
    }
    private int getC2() {
        System.out.println("子类C的普通属性初始化");
        return 4;
    }
}

结果:父类B的静态属性初始化
    父类B中的静态代码块
    子类C的静态属性初始化
    子类C中的静态代码块
    父类B的普通属性初始化
    父类B中的实例代码块
    父类B的无参构造器
    子类C的普通属性初始化
    子类C中的实例代码块
    子类C的无参构造器


static关键字

static是java中非常重要的一个关键字,而且它的用法也很丰富,主要有三种用法:

修饰成员变量

将其变为类的成员,从而实现所有对象对于该成员的共享;

static修饰成员变量

static修饰的变量也称为静态变量,静态变量和非静态变量的区别是:静态变量被所有对象共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

static成员变量的初始化顺序按照定义的顺序进行初始化。

修饰成员方法,将其变为类方法,可以直接使用“类名.方法名”的方式调用,常用于工具类;

static修饰成员方法:

static修饰的方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。

并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都必须依赖具体的对象才能够被调用。

但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的

this代表什么?this代表当前对象,那么通过new Main()来调用printValue的话,当前对象就是通过new Main()生成的对象。而static变量是被对象所享有的,因此在printValue中的this.value的值毫无疑问是33。在printValue方法内部的value是局部变量,根本不可能与this关联,所以输出结果是33。在这里永远要记住一点:静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)。 

修饰代码块

static关键字还有一个比较重要的作用就是用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来依次执行每个static块,并且只会执行一次。

static块可以优化程序性能,是因为它的特性:只会在类被初次加载的时候执行一次

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/2991615.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-23
下一篇 2022-09-23

发表评论

登录后才能评论

评论列表(0条)

保存