Java 类和对象——初识类和对象、封装、静态成员、内部类

    Java是面向对象(OOP)的语言。面向对象语言主要的三大特点:继承、多态和封装。自然的,对象,就是一个很重要的概念。面向对象是一种依靠对象之间的交互来完成一件事,这样的思想来写代码,符合人们的认知。

类和对象这一部分知识点零碎,而且抽象。需要转化为具体的,熟悉的例子去帮助理解。

现在假设要去洗衣服,面向过程和面向对象的不同:

    这就是面向过程和面向对象的不同。洗衣服的过程中,面向过程需要自己来做;而面向对象则只交给洗衣机就可以了。这个过程中,人,衣服,洗衣机都是一个不同的对象。我们不需要关系洗衣机是什么洗衣服的,只要把衣服放进去洗,依靠对象之间的交互,就完成了洗衣服这个过程。

    类是用来对一个实体(对象)来进行描述的。主要描述这个实体(对象)有什么属性(比如外观,颜色)和什么功能(比如标准洗衣,漂洗等)。简单来说,类相当于一个图纸。这个图纸说明了洗衣机的外观等属性,还说明了洗衣机的一些功能。

1、类的定义

通过class关键字来定义类。

// 创建类

class ClassName {

    field; // 字段(属性) 或者 成员变量

    method; // 行为 或者 成员方法

}

    class为定义类的关键字。ClassName为类的名字,建议使用大驼峰的命名方式。{}中是类的主体。类中包括成员变量(类的属性)和成员方法(类的功能)。

我们定义一个程序员类:

class Programmer {
    public String name;//姓名
    public int age;//年龄
    public String company;//公司
    public int hairCount;//头发数量
    
    public void work() {
        System.out.println("写代码");
    }
}

    上面的类名需要使用大驼峰命名形式。姓名、年龄等都是成员变量,这些成员变量在类的里面,方法的外面。而work则是成员方法。前面的public是公共权限的意思,这里暂时不讨论。

    一般来说,一个文件中只有一个类。但是在我们平常的练习中,为了方便,可以一个文件中写多个类。main方法所在的类用public修饰,这个类叫主类。public修饰的类要和文件名相同。

不要轻易去修改public修饰的类的名称,要修改,就要使用重构来进行修改,否则会出问题。

2、类的实例化

    定义了一个类,相当于在计算机中新定义了一种新的类型。这个类型和int、double类似,只不过这个类型是由用户自定义的。要使用这些类,就要对类进行实例化。前面我们说过,类相当于图纸,对类实例化,就是照着这个图纸造出新的东西,这个东西,叫对象。用类来创建对象的过程,叫类的实例化,使用new关键字来实例化一个对象。

class Programmer {
    public String name;//姓名
    public int age;//年龄
    public String company;//公司
    public int hairCount;//头发数量

    public void work() {
        System.out.println("写代码");
    }
}

public class test_03_26_05 {//本作者的主类

    public static void main(String[] args) {

        Programmer person1 = new Programmer();//创建了person1这个对象
    }
}

自定义类 对象名 = new 自定义类();

    这里对几个成员变量没有进行初始化,它在类的里面,方法的外面。没有对成员变量进行初始化的时候,就会使用它默认的值。这种也叫默认初始化。引用类型对应的是null,boolean类型对应的是false...

    当然,我们在创建的时候,也可以对他进行初始化,叫就地初始化,这种初始化是不建议的。这样的初始化在创建了对象后,每一个属性都带有初始化的内容。

class Programmer {
    public String name = "aaa";//姓名
    public int age = 18;//年龄
    public String company = "bbb";//公司
    public int hairCount = 0;//头发数量

    public void work() {
        System.out.println("写代码");
    }
}

使用.操作符来进行访问字段和方法。

class Programmer {
    public String name;//姓名
    public int age;//年龄
    public String company;//公司
    public int hairCount;//头发数量

    public void work() {
        System.out.println("写代码");
    }
}

public class test_03_26_05 {

    public static void main(String[] args) {
        Programmer person1 = new Programmer();
        person1.name = "张三";
        person1.age = 18;
        person1.work();
    }
}

说明:

(1)类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有哪些成员。

(2)类是一种自定义的类型,可以用来定义变量,但是在java中用类定义出来的变量实例化才成为对象。

(3)一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。

(4)类只是一个设计,要实例化出的对象才能实际存储数据,占用物理空间。

这个过程,在内存中发生了什么?

    person1是一个局部变量,在栈上,同时它也是一个引用变量。new了后,内存就会为person1分配内存。在类里面,方法外面的成员变量被分配到堆上,而成员方法里面的局部变量则在栈上开辟空间。

要对他进行初始化而不是就地初始化,除了一个一个访问去赋值外,还可以写一个方法来完成。

class Programmer {
    public String name;//姓名
    public int age;//年龄
    public String company;//公司
    public int hairCount;//头发数量

    //这个方法进行初始化
    public void SetProgrammer(String myName, int myAge, String myCompany, int myHairCount) {
        name = myName;
        age = myAge;
        company = myCompany;
        hairCount = myHairCount;
    }

    public void work() {
        System.out.println("写代码");
    }

    public void show() {
        System.out.println(name + " " + age + " " + company + " " + hairCount);
    }
}

public class test_03_26_05 {

    public static void main(String[] args) {
        Programmer person1 = new Programmer();
        person1.SetProgrammer("张三", 18, "a", 0);
        person1.work();
        person1.show();
    }
}

3、this关键字

现在有这样的一个情况,在上面的SetProgrammer方法中参数名称和成员变量名称相同。

class Programmer {
    public String name;//姓名
    public int age;//年龄
    public String company;//公司
    public int hairCount;//头发数量

    public void SetProgrammer(String name, int age, String company, int hairCount) {
        name = name;
        age = age;
        company = company;
        hairCount = hairCount;
    }

    public void work() {
        System.out.println("写代码");
    }

    public void show() {
        System.out.println(name + " " + age + " " + company + " " + hairCount);
    }
}

public class test_03_26_05 {

    public static void main(String[] args) {
        Programmer person1 = new Programmer();
        person1.SetProgrammer("张三", 18, "a", 0);
        person1.work();
        person1.show();
    }
}

 结果是

    发现,都是默认的值。SetProgrammer方法里面,好像写了赋值,实质上是把它本身的默认值赋值过去了,而不是形参里面的值。

    或者我们对同一个类进行多个实例化,那么在赋值的时候,编译器是什么知道该调用的哪一个对象呢,这些,都需要用到this关键字来进行解答。

    在写成员方法的时候,Java编译器自动给每一个方法增加了一个隐藏的引用类型参数,指向当前对象。在成员方法中的所有变量操作,都通过这个引用去访问。这个过程由编译器自动完成。现在我们加上看看。

 程序不受影响。给每一个变量加this看看。

    this关键字表示当前对象的引用。这里的当前对象指的是谁调用了成员方法,谁就是这个成员方法的对象,通过这个对象来引用成员方法。上面的show方法和work方法是由person1调用的,说明person1是“这两个方法的对象”,this表示这个当前对象引用了这两个方法。通过这个this引用,编译器就可以找到这两个方法。

要习惯使用this关键字,可以减少程序的错误。

总结:

(1)this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型。即ths保存着这个引用对象的值。

(2)this表示当前对象的引用,具有final(不可修改)属性。

(3)this是成员方法的第一个隐藏参数,编译器自动传递。在成员方法执行时,编译器会负责将调用成员方法 对象的引用传递给该成员方法,this负责来接收。this不能为空

(4)this的三个用法:this.属性表示访问成员变量、this.方法表示访问成员方法、this()。前两个表示当前引用,最后的this()表示调用其他的构造方法,且必须在相对的第一行,也不能形成环。

现在来了解构造方法。

4、构造方法

    我们在对象里面对参数进行赋值的时候,除了就地初始化、默认初始化和使用方法来完成初始化之外,还可以使用构造方法。

    构造方法(也称为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。它的方法名和类相同且没有返回值。

    我们new Programmer();就是在调用这个构造方法。当类里面没有写构造方法的时候,编译器会自动加一个不带参数的默认的构造方法,这个方法包括类的属性;如果写了带参数的构造方法,编译器就不会再自动加默认的构造方法,同时,想要调用默认的构造方法,必须要写出这个默认的构造方法。

使用我们写的show方法来打印它的结果。

 赋值的内容打印了出来,没有赋值的打印默认的值。

 不带参数:

构造方法只是对对象中的成员进行初始化,不负责给对象开辟空间。

总结:

(1)名字必须和类相同。

(2)没有返回值,就是void也不行。

(3)创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次。

(4)可以重载。

    现在来看看this()调用其他的构造方法。前面我们说过,this()表示调用其他的构造方法,必须在相对的第一行。

这样,就调用了带参数的构造方法。this()不能形成环,否者形成了构造器的递归。

5、封装

    面向对象的主要三大特性:封装、继承、多态。封装从字面上理解就是把一些细节给封住,就像一台洗衣机,我们看到的是这个洗衣机的外表、按钮和内部的滚筒。但是洗衣机按钮需要的电路、滚筒的机械装置等,都是看不到的,也就是说,这些具体的实现被封装了,只留下一些必要的按钮等。封装就是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。在比如我们使用的各种Java官方写好的方法,这些方法实现了封装,只留下必要的接口供使用者使用。

    Java使用类和对象的访问权限来实现封装。类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。我们前面写的public就是一种访问限定修饰符。Java中提供了四种访问限定符:

(1)public:公共权限,谁都可以使用;

(2)protected:受保护权限,常在继承中使用;

(3)default:默认权限, 在敲代码的时候不写default,直接是数据类型+变量名。

(4)private:私有权限,只能在本类中使用。

什么是包?

    在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于电脑上的文件夹。在写大型项目的时候,不可能只写一个文件和一个主类(主类名和文件名相同),往往是多个文件和多个主类组成一个文件夹。

    Java自带的包中有许多写好的类供我们使用。在使用这些类时需要加import关键字,表示导入包。我们对数组的操作也有它的工具类Arrays。

如果我们要自定义一个包,在项目中新建package。

    包名需要尽量指定成唯一的名字,通常会用公司的域名的颠倒形式(例如 com.naion.demo)。包名要和代码路径相匹配。例如创建 com.naion.demo 的包,那么会存在一个对应的路径 com/naion/demo来存储代码。如果一个类没有 package 语句,则该类被放到一个默认包中。

上面的权限对应了不同情况下的访问权限。

    类的实现者设置了权限private定义的age等只能在类当中使用,类的调用者不能访问到这些属性,这样就完成了封装。那如果想要对age等进行赋值和获取值呢?类的实现者只需要提供方法接口就可以了。提供接口的取值和赋值方法不能设置为private,否则类外访问不到这两个方法。

class Programmer {
    public String name;//姓名
    private int age;//年龄
    private String company;//公司
    private int hairCount;//头发数量

    //赋值
    public void setAge(int age) {
        this.age = age;
    }
    //得到值
    public int getAge() {
        return this.age;
    }
    
    public void show() {
        System.out.println(name + " " + age + " " + company + " " + hairCount);
    }
}

public class test_03_28_01 {

    public static void main(String[] args) {
        Programmer person1 = new Programmer();
        person1.name = "John";
        person1.setAge(18);
        System.out.println(person1.getAge());
        person1.show();
    }
}

6、static成员

我们的主方法main方法带有一个static,这表示静态的方法。

    现在我们假设在类里面有一个方法,不论创建了多少个对象,这个方法都是共有的,就像我们创建了很多的学生对象,每一个学生都有自己的姓名、年龄等,但是一个班里面的学生,有一个共有的老师。也就是说,在创造学生对象的时候,老师没必要创造很多个,只要一个就够了。这样,这个老师就使用static来修饰。(要注意,静态变量一般不会在构造方法中初始化,要进行初始化,第一种是下面的就地初始化,第二种是使用静态代码块初始化,见代码块)

 在Java中,被static修饰的成员,称之为静态成员,它有一些特点:

(1)不属于某个具体的对象,是所有对象共享的。

(2)静态变量也可以称为类变量。它在类的里面,对象的外面。不属于对象,属于类。也就是说,非静态的成员都依赖变量。

(3)类变量存储在方法区当中。方法区在JDK8之后,存储在Java的堆中。在逻辑上,方法区和堆是同级的,在实现上,方法区是堆的一部分。

(4)生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)

如果我们将teacher改为private权限,要访问teacher该如何访问?

静态的成员变量需要静态的成员方法去访问。我们可以写一个静态的成员方法。

    在调用静态的方法的时候,我们不必通过对象去调用,由于静态方法属于类,我们直接通过类名去访问,这也是推荐的写法。

总结:

(1)静态方法不属于某个具体的对象,是类方法。

(2)可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者。

(3)静态方法没有隐藏的this引用参数,因此不能在静态方法中访问任何非静态成员变量。

(4)非静态的成员方法可以使用静态的成员变量,但是静态的成员方法不能使用非静态的成员方法。

    这就是为什么在main方法调用的方法需要加public static。如果一个方法不加static,就会提示无法从静态上下文引用非静态的方法。由于静态成员变量属于类,所以我们对这个类实例化就可以使用了。

 7、代码块

    使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:普通代码块、构造块、静态块、同步代码块(多线程部分)。

普通代码块比较少见。

public class Main{
    public static void main(String[] args) {
    
        { //直接使用{}定义,普通方法块
            int x = 10 ;
            System.out.println("x1 = " +x);
        }
        int x = 100 ;
        System.out.println("x2 = " +x);
    }
}

// 执行结果
x1 = 10
x2 = 100

    构造代码块:定义在类中的代码块(不加修饰符),也叫实例代码块。构造代码块一般用于初始化实例成员变量,有点类似于构造方法。

public class Student{
    //实例成员变量
    private String name;
    private String gender;
    private int age;
    private double score;
    
    public Student() {
    System.out.println("I am Student init()!");
    }
    
    //实例代码块
    {
        this.name = "Naion";
        this.age = 20;
        this.sex = "man";
        System.out.println("I am instance init()!");
     }
     public void show(){
        System.out.println("name: "+name+" age: "+age+" sex: "+sex);
     }
}

public class Main {

    public static void main(String[] args) {
        
        Person p1 = new Person();
        p1.show();
    }
}

// 运行结果
I am instance init()!
I am Student init()!
name: Naion age: 20 sex: man

    实例代码块优先于构造方法执行,因为编译完成后,编译器会将实例代码块中的代码拷贝到每个构造方法第一条语句前。

使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。

public class Student{
    private String name;
    private String gender;
    private int age;
    private double score;
    private static String classRoom;

    // 静态代码块
    static {
        classRoom = "Naion";
        System.out.println("I am static init()!");
    }
    public Student(){
        System.out.println("I am Student init()!");
    }
    public static void main(String[] args) {
        Student s1 = new Student();
        Student s2 = new Student();
    }
}

    静态代码块不管生成多少个对象,其只会执行一次,静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的。

8、内部类

    一个事务的内部,还有一部分需要一个完整的结构来进行描述,而这个内部的结构又只为外部类服务,那么就可以使用内部类。比如说计算机的图纸,是由芯片、显示屏等组成,而芯片也有它制造图纸。这样,计算机相当于外部的类,芯片相当于计算机这个外部的类中的类,成为内部类。内部类也是封装的一种。

class OutClass {
    private int a;

    class InnerClass{
        private int b;
    }
} 
// OutClass是外部类
// InnerClass是内部类

    内部类必须在类的内部(外部类{ 内部类 })。内部类和外部类共用一个java源文件,但是内部类会单独产生自己的字节码文件。

    内部类分为:实例内部类、静态内部类、局部内部类和匿名内部类。着重了解的是实例内部类和静态内部类,这里不介绍局部内部类和匿名内部类。原因是前者用的较少,后者需要新的知识(接口)。

8.1、实例内部类

没有被static修饰的内部类。

class OuterClass {
    public int data1 = 10;
    private int data2 = 20;
    public static int data3 = 30;
    public int data7 = 71;

    class InnerClass {
        public int data4 = 40;
        private int data5 = 50;
        public int data7 = 70;
    }
}

8.1.1、如何实例化实例内部类的对象?

    内部类是套在外部类的里面的,需要先对外部类进行实例化对象,然后再对内部类进行实例化对象。

public static void main(String[] args) {

        //外部类实例化
        OuterClass out = new OuterClass();
        //外部类.内部类 内部对象的引用 = 外部类对象的引用.new 内部类();
        OuterClass.InnerClass inn = out.new InnerClass();
        
        //这样的写法一步到位
        //OuterClass.InnerClass inn = new OuterClass().new InnerClass();
}

我们打印内部类InnerClass中的data4,就可以直接通过.操作符来进行访问。

System.out.println(inn.data4);//40

8.1.2、如何在实例内部类中定义静态的成员?

我们在上面的类中追加一个data6 = 60,看看效果:

    报错了,前一个的原因是在编译的时候,外部类实例化被加载到jvm里面时,内部类还没有被实例化,解决办法是加一个final关键字,表示把静态的变量变成静态的常量,它不存在于栈中;后者的原因是内部类没有被加载,在堆中没有这个方法。不管加不加final,都会报错。

8.1.3、如何在实例内部类中访问外部类的成员

内部类是外部类的一部分,写一个方法直接访问即可。

class OuterClass {
    public int data1 = 10;
    private int data2 = 20;
    public static int data3 = 30;

    class InnerClass {
        public int data4 = 40;
        private int data5 = 50;
        public static final int data6 = 60;

        public InnerClass() {
            System.out.println("InnerClass的构造方法");
        }

        public void insideGetOutsideMethod() {
            System.out.println("insideGetOutsideMethod方法");
            System.out.print(data1 + " ");
            System.out.print(data2 + " ");
            System.out.print(data3 + " ");
            System.out.print(data4 + " ");
            System.out.print(data5 + " ");
            System.out.print(data6 + " ");
        }
    }
}

public class Main {

    public static void main(String[] args) {

        //外部类
        OuterClass out = new OuterClass();
        //外部类.内部类 内部类名 = 外部类对象的引用.new 内部类();
        OuterClass.InnerClass inn = out.new InnerClass();
        //OuterClass.InnerClass inn = new OuterClass().new InnerClass();

        //System.out.println(inn.data4);
        
        inn.insideGetOutsideMethod();

    }
}

8.1.4、如何在外部类中访问实例内部类的成员

在外部类中写一个方法,需要在这个方法中实例化这个内部类,在去访问。

class OuterClass {
    public int data1 = 10;
    private int data2 = 20;
    public static int data3 = 30;

    class InnerClass {
        public int data4 = 40;
        private int data5 = 50;

        public static final int data6 = 60;

        public InnerClass() {
            System.out.println("InnerClass的构造方法");
        }

        public void insideGetOutsideMethod() {
            System.out.println("insideGetOutsideMethod方法");
            System.out.print(data1 + " ");
            System.out.print(data2 + " ");
            System.out.print(data3 + " ");
            System.out.print(data4 + " ");
            System.out.print(data5 + " ");
            System.out.print(data6 + " ");
        }
    }

    public void outsideGetsInsideMethod() {
        InnerClass inn = new InnerClass();
        System.out.print(inn.data4 + " ");
    }
}


public class Main {

    public static void main(String[] args) {

        //外部类
        OuterClass out = new OuterClass();
        //外部类.内部类 内部类名 = 外部类对象的引用.new 内部类();
        OuterClass.InnerClass inn = out.new InnerClass();
        //OuterClass.InnerClass inn = new OuterClass().new InnerClass();
        //System.out.println(inn.data4);

        out.outsideGetsInsideMethod();

    }
}

8.1.5、如果实例内部类和外部类成员属性名称相同的情况

现在我们新加一个data7,外部类中,data7 = 71,实例内部类中,data7 = 70。

首先在内部类方法insideGetOutsideMethod()中打印data7。

发现打印的是内部类的值70。要访问外部类的data7,需要外部类名.this.data7。

    外部类名.this.data7缺一不可。外部类类名和data7好理解,这个this是因为内部类中的this不仅是自己的this,也是外部类的this。如果去掉this,会报错:不能从非静态的上下文获得静态的成员,原因是静态成员直接通过类名去访问。去掉了this,编译器认为是静态访问。

在外部类方法outsideGetsInsideMethod()中打印data7,需要实例化这个实例内部类对象。

不能使用内部类名.this.data7去外部类的方法中访问内部类data7的值。

总结:

(1)外部类的任何成员都可以在实例内部类的方法中直接访问。

(2)实例内部类所处的位置和外部类是相同的,也受到publc等访问限定符的约束。

(3)实例内部类中访问同名的成员时,优先访问自己的,要访问外部的,外部类名
称.this.同名成员 来访问。

(4)实例内部类需要首先在外部类实例化对象为前提下才能创建。

(5)实例内部类中的非静态方法包含一个指向外部类对象的引用。

(6)外部类中需要实例化实力内部类对象才能访问实例内部类的属性和方法。

8.2、静态内部类

被static修饰的内部成员类称为静态内部类。静态内部类使用较多。

class OuterClass {
    public int data1 = 10;
    private int data2 = 20;
    public static int data3 = 30;

    static class InnerClass {
        public int data4 = 40;
        private int data5 = 50;
        public static int data6 = 60;
    }
}

8.2.1、如何实例化静态内部类的对象?

    静态成员不依赖对象,属于类,所以创建静态内部类时,可以直接通过外部类类名去访问,不需要创建外部类的对象。

class OuterClass {
    public int data1 = 10;
    private int data2 = 20;
    public static int data3 = 30;

    static class InnerClass {
        public int data4 = 40;
        private int data5 = 50;
        public static int data6 = 60;

        public void innerClassMethod() {
            System.out.println("静态内部类成员方法");
        }

        public static void innerClassStaticMethod() {
            System.out.println("静态内部类静态成员方法");
        }

    }

    public void OuterClassMethod() {
        System.out.println("外部类成员方法");
    }

}

public class Main {

    public static void main(String[] args) {

        //外部类类名.静态内部类类名 对象引用 = new 外部类类名.内部类类名();
        OuterClass.InnerClass inn = new OuterClass.InnerClass();
        System.out.println(inn.data4);
        //静态的成员属性直接通过类访问
        System.out.println(OuterClass.InnerClass.data6);
    }
}

8.2.2、如何在静态内部类中访问外部类的成员

我们写一个方法,在静态内部类中访问静态内部类和外部类的属性。

class OuterClass {
    public int data1 = 10;
    private int data2 = 20;
    public static int data3 = 30;

    static class InnerClass {
        public int data4 = 40;
        private int data5 = 50;
        public static int data6 = 60;

        public void innerClassMethod() {
            System.out.println("静态内部类成员方法");
        }

        public static void innerClassStaticMethod() {
            System.out.println("静态内部类静态成员方法");
        }

        public void insideGetOutsideMethod() {

            OuterClass out = new OuterClass();
            System.out.print(out.data1 + " ");
            //System.out.println(data1);

            System.out.print(data3 + " ");
            System.out.print(data4 + " ");
            System.out.println(data6 + " ");
        }

    }

    public void outerClassMethod() {
        System.out.println("外部类成员方法");
        System.out.print(data1 + " ");

        InnerClass inn = new InnerClass();
        System.out.print(inn.data4 + " ");
        //静态内部类中的静态成员直接通过静态内部类类名访问
        System.out.print(InnerClass.data6 + " ");
    }

}

public class Main {

    public static void main(String[] args) {

        //外部类类名.静态内部类类名 对象引用 = new 外部类类名.内部类类名();
        OuterClass.InnerClass inn = new OuterClass.InnerClass();
        inn.insideGetOutsideMethod();
        
    }
}

    我们发现:在静态内部类中访问外部类的非静态成员属性是不能访问的,静态成员属性是可以的;在静态内部类中访问自己的属性是可以的。

那我要是非要在静态内部类中访问非静态的成员变量呢?实例化这个外部类。

8.2.3、如何在外部类中访问静态内部类的成员

对静态内部类进行实例化对象。

class OuterClass {
    public int data1 = 10;
    private int data2 = 20;
    public static int data3 = 30;

    static class InnerClass {
        public int data4 = 40;
        private int data5 = 50;
        public static int data6 = 60;

        public void innerClassMethod() {
            System.out.println("静态内部类成员方法");
        }

        public static void innerClassStaticMethod() {
            System.out.println("静态内部类静态成员方法");
        }

        public void insideGetOutsideMethod() {

            OuterClass out = new OuterClass();
            System.out.print(out.data1 + " ");
            //System.out.println(data1);

            System.out.print(data3 + " ");
            System.out.print(data4 + " ");
            System.out.println(data6 + " ");
        }

    }

    public void outerClassMethod() {
        System.out.println("外部类成员方法");
        System.out.print(data1 + " ");

        InnerClass inn = new InnerClass();
        System.out.print(inn.data4 + " ");
        //静态内部类中的静态成员直接通过静态内部类类名访问
        System.out.print(InnerClass.data6 + " ");
    }

}

public class Main {

    public static void main(String[] args) {

        //外部类类名.静态内部类类名 对象引用 = new 外部类类名.内部类类名();
//        OuterClass.InnerClass inn = new OuterClass.InnerClass();
//        inn.insideGetOutsideMethod();
        OuterClass out = new OuterClass();
        out.outerClassMethod();
    }
}

8.2.4、如果静态内部类和外部类成员属性名称相同的情况 

现在我们新加一个data7,外部类中,data7 = 71,静态内部类中,data7 = 70。

    首先在内部类方法insideGetOutsideMethod()中打印data7,打印的是静态内部类中的data7的70,要打印外部类的同名变量,就先要实例化这个对象,通过这个对象去访问。静态成员不依赖对象,因此,this关键字是不起作用的。

在外部类outerClassMethod()中打印data7,打印的是外部类data7的71。要打印静态内部类的data7,需要实例化这个对象,通过这个实例化的对象去访问。

总结

(1)在静态内部类中只能访问外部类的静态成员,要访问非静态成员,就要实例化外部类对象。

(2)创建静态内部类时,不需要先创建外部类对象。

局部内部类和匿名内部类在这里不做介绍。

9、打印对象

    我们知道,打印数组中的内容,除了可以使用循环来进行打印之外,还可以使用Arrays.toString方法去打印。那么,有没有类似于Arrays.toString方法来直接打印对象呢?

class Person {
    String name;
    String sex;
    int age;

    public Person(String name, String sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

}

public class Main {

    public static void main(String[] args) {
        Person person = new Person("Naion", "nan", 20);
        System.out.println(person);//对象的地址
    }
}

 我们只需要重写这个toString方法就可以了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值