不就是Java吗类和对象 Part II

本文详细介绍了Java中的对象构造,包括构造方法、默认初始化、就地初始化,并探讨了封装的概念,通过猫类的例子展示了如何实现封装。此外,还讲解了包的使用、静态成员以及代码块的作用,最后提及了内部类的分类和用法。
摘要由CSDN通过智能技术生成

5、对象的构造及其初始化

5.1 如何初始化对象?

我们知道,在之前的Date这个类中,我们需要调用setDate这个方法来初始化。其实我们还有好几种方法,我们先给大家介绍一下构造方法

5.2 构造方法

5.2.1 概念

  1. ⽅法名与类名相同
  2. 没有返回值

举个栗子:

public Date() {
    
    }

这就是一个最简单的不带参数的构造方法。在创建对象的时候,由编译器自动调用,并且在整个对象的生命周期内只调用一次。

当我们实例化一个对象的时候,必须有以下这两部,但不是一定只有这两步

  1. 为对象分配内存

  2. 调用合适的构造方法?

    这句话其实另有玄机,代表构造方法不一定只有一个

public class Date {
    public int year;
    public int month;
    public int day;

    //不带参数的构造方法
    public Date() {
        System.out.println("调用不带参数的构造方法");
    }

    //带三个参数的构造方法
    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
        System.out.println("调用带三个参数的构造方法");
    }

    //打印日期
    public void printDate() {
        System.out.println(year + "年" + month + "月" + day + "日");
    }

    public static void main(String[] args) {
        //调用不带参数的构造方法
        Date date1 = new Date();
        //调用带三个参数的构造方法
        Date date2 = new Date(2022,05,24);
    }
}

image-20220524223407516

image-20220524223700120

那么我们要注意:当程序没有构造方法的时候,编译器会默认帮我们生成一个不带参数的构造方法。如果你自己写了构造方法,那么编译器就不会再自动生成一个不带参数的构造方法了,我们可以通过下面的代码进行检验

public class Date {
    public int year;
    public int month;
    public int day;
    
    //带三个参数的构造方法
    public Date(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
        System.out.println("调用带三个参数的构造方法");
    }

    //打印日期
    public void printDate() {
        System.out.println(year + "年" + month + "月" + day + "日");
    }

    public static void main(String[] args) {
        //调用不带参数的构造方法
        Date date1 = new Date();
    }
}

image-20220524224131954

我们可以看见,这段代码中,我们自己写了一个带三个参数的构造函数,然后main方法中却调用了不带参数的构造函数,通过图片我们得知,这报错了,是因为我们自己生成构造函数的话,编译器就不会再帮我们生成不带参数的构造函数,所以在主函数调用不带参数的构造函数会报错

5.2.2 特性

  1. 名字必须与类名相同

    image-20220524224600818

  2. 没有返回值类型,设置为void也不行

    image-20220524224640612

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

  4. 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)

    重载:方法名相同,参数裂变不同,和返回值没有关系

    image-20220524224953676

  5. 如果用户没有自己定义构造函数,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的。(所以一个类是一定至少含有一个构造函数)

  6. 编译器帮我们实现的构造函数:右键->generate->constructer->选中要操作的属性

    编译器帮我们实现show函数:右键->generate->toString()

  7. 构造方法中,可以通过this调用其他构造方法来简化代码

    class Student {
        public String name;
        public int age;
        public double score;
        public String sex;
    
        //不带参数的构造函数
        public Student() {
            this("剪秋",22,98,"女");//调用Student类中带4个参数的构造函数
        }
    
        //带三个参数的构造函数
        public Student(String name, int age, double score) {
            this.name = name;
            this.age = age;
            this.score = score;
        }
    
        //带四个参数的构造函数
        public Student(String name, int age, double score, String sex) {
            this.name = name;
            this.age = age;
            this.score = score;
            this.sex = sex;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", score=" + score +
                    ", sex='" + sex + '\'' +
                    '}';
        }
    }
    public class StudentDemo {
        public static void main(String[] args) {
            Student student1 = new Student();
            System.out.println(student1);
        }
    }
    
    

    image-20220524225842892

    注意:

    1. this必须放在构造函数的第一行

      public Student() {
              System.out.println("this只能放在第一行");
              this("剪秋",22,98,"女");//调用Student类中带4个参数的构造函数
          }
      

      image-20220524230048690

    2. 一个构造函数里面只能有一个this

      public Student() {
              this("剪秋",22,98,"女");//调用Student类中带4个参数的构造函数
              this("宝娟"2577);
          }
      

      image-20220524230322145

    3. 不能成环

      //不带参数的构造函数
          public Student() {
              this("剪秋",22,98);//调用Student类中带4个参数的构造函数
          }
      
          //带三个参数的构造函数
          public Student(String name, int age, double score) {
              this();
              this.name = name;
              this.age = age;
              this.score = score;
          }
      

      image-20220524230438021

5.3 默认初始化

我们之前写过的代码就是默认初始化的

public class Date {
    //默认初始化:初始化为对应的null
    public int year;
    public int month;
    public int day;

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

5.4 就地初始化

public class Date {
    //就地初始化:不太好
    public int year = 2022;
    public int month = 05;
    public int day = 24;

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

代码编译完成后,编译器会将所有就地初始化的语句添加到各个构造函数中

博客分隔符-笑脸

就目前为止,我们针对目前所学习的知识,来创建一个猫的类吧

class Cat {
    public String name;
    public int age;
    public String color;

    public Cat() {
        //可以在这里面进行初始化
        this.name = "哈哈";
        this.age = 3;
        this.color = "黑色";
        System.out.println("带一个参数的构造方法");
    }

    public Cat(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
        System.out.println("带三个参数的构造方法");
    }

    public void sleep() {
        System.out.println(this.name + "正在睡觉");
    }

    public void eat() {
        System.out.println(this.name + "正在吃饭");
    }
}
public class CatDemo {
    public static void main(String[] args) {
        Cat cat1 = new Cat("啦啦",4,"白色");
        cat1.sleep();
        cat1.eat();

        Cat cat2 = new Cat();
        cat2.sleep();
        cat2.eat();
    }
}

image-20220524232121581

6、封装

6.1 封装的概念

面向对象程序三大特性:封装、继承、多态。而类和对象阶段,主要研究的就是封装特性。封装呢,简单来说,就是套壳屏蔽细节。

比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器,USB插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU、显卡、内存等一些硬件元件。

对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互(公开的方法 和 私有的属性之间结合)

封装还可以降低代码的耦合度(意思就是关联程度,比如:name改成myname影响不会特别大)

6.2 封装实例:猫类

我们刚刚完成了一个猫类,现在,我们给他改成封装的思考方式

class Cat {
    //1.我们先把访问修饰限定符改成private
    //这些被private修饰的 属性 或者 方法,只能在当前Cat当中使用->安全,类外不课件
    //对类的实现细节 进行隐藏
    private String name;
    private int age;
    private String color;

    //2.我们可以生成一些get set 方法
    //右键->Generate->getter and setter

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    //之前写过的构造函数
    public Cat() {
        //可以在这里面进行初始化
        this.name = "哈哈";
        this.age = 3;
        this.color = "黑色";
        System.out.println("带一个参数的构造方法");
    }

    public Cat(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
        System.out.println("带三个参数的构造方法");
    }

    //之前自己定义的方法
    public void sleep() {
        System.out.println(this.name + "正在睡觉");
    }

    public void eat() {
        System.out.println(this.name + "正在吃饭");
    }

    //生成一个show方法
    //右键->Generate->toString()
    //打印:System.out.println(cat);

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }
}
public class CatDemo {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.setName("嘻嘻");//又可以通过刚刚生成的setter方法把名字传过去
        String name = cat.getName();//可以通过getter方法获取到名字
        System.out.println(name);
        System.out.println("--------------");
        //name age color是私有属性,类外访问不到
        //cat.name = "嘻嘻";
        //cat.age = 10;
        //cat.color = "⻩⾊";
        System.out.println(cat);
    }
}

我们可以看到,类里面的属性被设置成了私有,但是我们还生成了一系列gettersetter方法来给类的使用者去获取到相关信息,所以这就是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

那么现在,在主函数我们想要直接访问nameagecolor方法是访问不到的,只能通过gettersetter方法

image-20220526152200757

小猫分隔符

我们接下来继续看,如果我们传入的对象是空的时候,是什么效果?

public static void main(String[] args) {
        Cat cat = null;
        System.out.println(cat);//传过去null,打印的就是字符串null
    }

我们看运行结果,是null

image-20220526152618544

博客分隔符-笑脸

我们在输入数据的时候,同样可以采用构造方法

public static void main(String[] args) {
        Cat cat1 = new Cat("华妃",4,"橘⻩⾊");
        cat1.show();
        cat1.sleep();
        Cat cat2 = new Cat("欣贵人",14,"红色");
        cat2.show();
        cat2.sleep();
        Cat cat3 = new Cat();
        cat3.sleep();
        cat3.show();
    }

image-20220526153118837

但是奇怪的是,cat3我们并没有初始化啊,为什么它叫哈哈了?

实际上,我们看上面的带一个参数的构造函数

public Cat() {
     //可以在这里面进行初始化
     this.name = "哈哈";
     this.age = 3;
     this.color = "黑色";
     System.out.println("带一个参数的构造方法");
 }

我们在这里面也进行过初始化,如果我们之后有传入其他的数据,那么就使用我们传过来的数据,要不就使用这里面构造的数据

6.3 包

为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。把多个类收集在一起成为一组,称为软件包,有点类似于目录。

包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。

6.3.1 导入包中的类

Java 中已经提供了很多现成的类供我们使用. 例如Date类:可以使用 java.util.Date 导入java.util这个包中的 Date类.

public class TimeStamp {
    public static void main(String[] args) {
        java.util.Date date = new java.util.Date();
        //得到一个毫秒级别的时间戳
        System.out.println(date.getTime());
    }
}

我们可以通过import语句简化写法

import java.util.Date;

public class TimeStamp {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.getTime());
    }
}

如果需要使用 java.util 中的其他类, 可以使用 import java.util.*

import java.util.*;//导⼊util的包,但是只会访问需要的那个功能

public class TimeStamp {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.getTime());//时间戳需要用到util下面的功能
        System.out.println("---------------");
        int[] array = {1,3,5,7,9};
        System.out.println(Arrays.toString(array));//toString也需要用到util下面的功能
    }
}

但是有可能,Date功能在util包和sql包下面都有,恰好我们都import了,那么就会报错

import java.util.*;
import java.sql.*;

public class TimeStamp {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.getTime());
    }
}

image-20220526154846070

那么我们需要显示指定才能解决,像下面这样

import java.util.*;
import java.sql.*;

public class TimeStamp {
    public static void main(String[] args) {
        java.util.Date date = new java.util.Date();
        System.out.println(date.getTime());
    }
}

我们还可以可以使用import static导入包中静态的方法和字段。(不推荐)

import static java.lang.Math.*;

public class TimeStamp {
    public static void main(String[] args) {
        double x = 30;
        double y = 40;
        // 静态导入的方式写起来更方便一些.
        // double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        double result = sqrt(pow(x, 2) + pow(y, 2));
        System.out.println(result);
    }
}

注意事项: import C++#include 差别很大。C++ 必须 #include 来引入其他文件内容,,但是 Java 不需要,import 只是为了写代码的时候更方便。import 更类似于 C++ namespaceusing

6.3.2 自定义包

⼀般全部小写,公司域名逆序

  1. 基本规则

    • 在文件的最上方加上一个 package 语句指定该代码在哪个包中.

    • 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.baidu.demo )

    • 包名要和代码路径相匹配. 例如创建 com.baidu.demo 的包, 那么会存在一个对应的路径 com/baidu/demo 来存储
      代码.

    • 如果一个类没有 package 语句, 则该类被放到一个默认包中

  2. 创建方法

    1. src->右键->New->Package

      image-20220526160928156

    2. 公司域名逆序,全部小写,中间用.分隔

      image-20220526162659903

    3. 这时候我们就可以看见文件夹创建了

      image-20220526162733596

6.3.3 访问限定符

image-20220526163323648

public:公共的,公开的,哪里都能访问

default:什么都不写,只能在同一个包里面访问

private:只能在当前类访问

具体的访问修饰限定符我们下一篇博客会详细讲解

6.3.4 包的访问权限控制举例

Computer类位于pack.test.demo包中,TestComputer位置pack.test.demo2包中:

Computer:

package pack.test.demo;

public class Computer {
    private String cpu;//cpu
    private String memory;//内存
    public String screen;//屏幕
    String brand;//品牌

    public Computer(String cpu, String memory, String screen, String brand) {
        this.cpu = cpu;
        this.memory = memory;
        this.screen = screen;
        this.brand = brand;
    }
    
    public void PowerOn() {
        System.out.println("开机");
    }
    
    public void PowerOff() {
        System.out.println("关机");
    }
    
    public void SurfInternet() {
        System.out.println("上网");
    }
}

TestDemo:

package pack.test.demo2;

import pack.test.demo.Computer;//引用了pack.test.demo里面的Computer这个类

public class TestComputer {
    public static void main(String[] args) {
        Computer computer = new Computer("CPU","MEMORY","SCREEN","BRAND");
        System.out.println(computer.screen);//public 到处可以访问
        //System.out.println(computer.cpu);//private 只能在当前类访问
        //System.out.println(computer.brand);//默认权限 只能在同一个包下访问
    }
}

image-20220526165201545

6.3.5 常见的包

  1. java.lang:系统常用基础类(String,object)
  2. java.lang.reflect:Java反射基础包
  3. java.net:网络编程开发包
  4. java.sql:数据库开发支持包
  5. java.util:工具程序包(集合类等),非常重要
  6. java.io:I/O编程开发包

7、static成员

7.1 引入

我们之前创建的Student类,里面有以下这四个属性

public String name;
public int age;
public double score;
public String sex;

那么我们知道,每个人的姓名、年龄、分数和性别可能都是不同的,那么如果这几个同学都是一个班级的,那该怎么表示?

我们仍然可以创建一个成员变量,然后分别赋值。我们也可以创建一个static修饰的成员变量,被static修饰的成员变量,称之为静态成员(类成员),他不属于某个具体的对象,使所有对象所共享的

7.2 static修饰成员变量

static修饰的成员变量,称为静态成员变量。

它最大的特性就是:不属于某个具体的对象,是所有对象所共享的

那么他的特性都有:

  1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中

  2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问,而且这样也不需要new对象了

    Student.class;//推荐
    student.class;//不推荐
    
  3. JDK7及以前,HotSpot(Java虚拟机)中存储在方法区,JDK8及之后,类变量存储在Java堆中

    其实方法区和堆逻辑上都属于同一个等级的关系,只不过在JVM当中实现这个东西的时候,把他放在了堆里

  4. 类变量存储在方法区当中

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

    类在的话就在,不在就没了

举个栗子吧:

class Student {
    //成员变量(字段/属性)
    public String name;
    public int age;
    public double score;
    public String sex;
    public static String classRoom = "0301";//静态的成员变量

    public Student(String name, int age, double score, String sex) {
        this.name = name;
        this.age = age;
        this.score = score;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                ", sex='" + sex + '\'' +
                ", class='" + classRoom +'\'' +
                '}';
    }
}
public class StudentDemo {
    public static void main(String[] args) {
        Student student1 = new Student("甄嬛",20,98,"女");
        Student student2 = new Student("沈眉庄",21,97,"女");
        Student student3 = new Student("安陵容",22,99,"女");
        System.out.println(Student.classRoom);//推荐:类名.静态成员变量
        System.out.println("---------------");
        //也可以通过对象.静态成员变量访问
        System.out.println(student1.classRoom);
        System.out.println(student2.classRoom);
        System.out.println(student3.classRoom);
    }
}

image-20220526205054097

那么我们可以调试一下,看看静态的成员变量是否存在于某个具体的变量当中

image-20220526205307435

7.3 static修饰成员方法

正常情况下,一个类当中成员变量是要设置成private ,成员方法是要设置成public的,但是private修饰之后,在类外是访问不到的。那么假如要是把静态成员变量设置成private修饰的,在类外怎么访问呢?

class Student2 {
    //成员变量(字段/属性)
    public String name;
    public int age;
    public double score;
    public String sex;
    private static String classRoom = "0301";//静态的成员变量,被private修饰了
}
public class StudentDemo2 {
    public static void main(String[] args) {
        System.out.println(Student.classRoom);
    }
}

image-20220526205940985

我们可以通过静态的成员方法,来实现在类外访问被private修饰的静态成员变量

class Student2 {
    //成员变量(字段/属性)
    public String name;
    public int age;
    public double score;
    public String sex;
    private static String classRoom = "0301";//静态的成员变量,被private修饰了

    public static String getClassRoom() {
        return classRoom;
    }
}
public class StudentDemo2 {
    public static void main(String[] args) {
        System.out.println(Student2.getClassRoom());
    }
}

静态方法的特性:

  1. 不属于某个具体的对象,是类方法

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

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

    class Student2 {
        //成员变量(字段/属性)
        public String name;
        public int age;
        public double score;
        public String sex;
        private static String classRoom = "0301";//静态的成员变量,被private修饰了
    
        public Student2(String name, int age, double score, String sex) {
            this.name = name;
            this.age = age;
            this.score = score;
            this.sex = sex;
        }
    
        public static String getClassRoom() {
            this("齐妃",30,88,"女");//静态成员方法里面是不能调用this方法的
            age = age + 1;//静态成员方法里面是不能引用非静态变量的
            System.out.println(classRoom);//静态成员方法里面可以引用静态成员变量
            return classRoom;
        }
    }
    public class StudentDemo2 {
        public static void main(String[] args) {
            System.out.println(Student2.getClassRoom());
        }
    }
    

    image-20220526211822652

  4. 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用

    class Student2 {
        //成员变量(字段/属性)
        public String name;
        public int age;
        public double score;
        public String sex;
        private static String classRoom = "0301";//静态的成员变量,被private修饰了
    
        public static String getClassRoom() {
            //doClass();//静态成员方法当中不能调用普通方法
            return classRoom;
        }
        
        public void doClass() {
            System.out.println(this.name + "正在上课");
        }
    }
    public class StudentDemo2 {
        public static void main(String[] args) {
            System.out.println(Student2.getClassRoom());
        }
    }
    
    

    image-20220526212147346

  5. 通过对象.成员变量可能会产生问题

    public static void main(String[] args) {
            Student student = null;//student不指向任何对象
            student.classRoom = "0302";//student不指向任何对象,所以student.classRoom不知道指向的是哪里,就被赋值成0302了
            System.out.println(student.classRoom);
        }
    

7.4 static成员变量初始化

我们要初始化静态的成员变量的时候,一般不会使用构造函数,构造方法中初始化的是与对象相关的实例属性

静态成员变量的初始化分为两种:就地初始化和静态代码块初始化。

就地初始化就是刚才那几个例子

private static String classRoom = "0301";

静态代码块初始化,在学习他之前,还得学学代码块

8、代码块

8.1 分类

8.1.1实例代码块/构造代码块

实例代码块是优先于构造方法执行的。实际上从字节码的角度来看,是把实例代码块当中的内容,拷贝到了构造方法之前。一般用来初始化普通的成员变量

当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行

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

    //构造方法
    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
        System.out.println("构造方法:带三个参数");
    }

    //实例代码块
    {
        this.name = "瑛贵人";
        this.age = 22;
        this.score = 88.8;
        System.out.println("实例代码块:比构造方法要先执行,一般用来初始化普通的成员变量");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Student student1 = new Student("华妃",18,99.9);
        
    }
}

image-20220526222227581

8.1.2 静态代码块

先执行静态代码块,再执行实例代码块。静态代码块只会执行一次。如果写了多个静态代码块,会根据书写的先后顺序来进行赋值(其实就是相当于之前的被复制了)

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

    //构造方法
    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
        System.out.println("构造方法:带三个参数");
    }

    //实例代码块
    {
        this.name = "瑛贵人";
        this.age = 22;
        this.score = 88.8;
        System.out.println("实例代码块:比构造方法要先执行,一般用来初始化普通的成员变量");
    }

    //静态代码块
    static {
        classes = "0301";
        System.out.println("静态代码块:最先执行,一般用来初始化静态成员变量");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Student student1 = new Student("华妃",18,99.9);
    }
}

image-20220526222746722

静态代码块的注意事项:

  1. 静态代码块也是不依赖对象的,里面不能出现非静态成员

    //静态代码块
    static {
        this("富察贵人",78,11);//静态代码块里面不能出现this
        age = age + 1;//静态代码块里面不能出现非静态成员变量
        classes = "0301";
        System.out.println("静态代码块:最先执行,一般用来初始化静态成员变量");
    }
    

    image-20220526223215736

  2. 出现两个静态代码块的时候,打印的结果是最后一个static的值(之前的被覆盖了)

    class Student {
        public String name;
        private int age;
        private double score;
        public static String classes;
    
        //构造方法
        public Student(String name, int age, double score) {
            this.name = name;
            this.age = age;
            this.score = score;
            System.out.println("构造方法:带三个参数");
        }
    
        //实例代码块
        {
            this.name = "瑛贵人";
            this.age = 22;
            this.score = 88.8;
            System.out.println("实例代码块:比构造方法要先执行,一般用来初始化普通的成员变量");
        }
    
        //静态代码块
        static {
            classes = "0301";
        }
        static {
            classes = "0302";
        }
    }
    public class TestDemo {
        public static void main(String[] args) {
            System.out.println(Student.classes);
        }
    }
    

    image-20220526223613168

  3. 在普通的成员方法里面是可以使用静态的成员变量的

    class Student {
        public String name;
        private int age;
        private double score;
        public static String classes;
    
        //构造方法
        public Student(String name, int age, double score) {
            this.name = name;
            this.age = age;
            this.score = score;
            System.out.println("构造方法:带三个参数");
        }
    
        public void doClass() {
            //在普通的成员方法里面是可以使用静态局部变量的
            System.out.println(classes);
        }
    }
    public class TestDemo {
        public static void main(String[] args) {
            Student student1 = new Student("华妃",18,99.9);
            student1.doClass();
        }
    }
    

    image-20220526224105507

  4. 静态的成员方法中,不能访问非静态的成员

    public static void func() {
            //在静态成员函数里面不能访问非静态成员
            doclass();
            System.out.println(age);
            //在静态成员函数里面访问静态局部变量可以
            System.out.println(classes);
        }
    

    image-20220526224424288

  5. 静态代码块先执行,并且只执行一次,在类加载阶段执行

    class Person {
        public String name;
        public int age;
        public Person(String name,int age) {
            this.name = name;
            this.age = age;
            System.out.println("构造方法");
        }
    
        {
            System.out.println("实例代码块");
        }
    
        static {
            System.out.println("静态代码块");
        }
    }
    public class TestDemo2 {
        public static void main(String[] args) {
            Person person1 = new Person("瑛贵人",20);
            System.out.println("----------------------");
            Person person2 = new Person("浣碧",22);
        }
    }
    
    

    image-20220527151043230

  6. 实例代码块只有在创建对象的时候执行

    class Student {
        public String name;
        private int age;
        private double score;
        public static String classes;
    
        //构造方法
        public Student(String name, int age, double score) {
            this.name = name;
            this.age = age;
            this.score = score;
            System.out.println("构造方法:带三个参数");
        }
    
        //实例代码块
        {
            this.name = "瑛贵人";
            this.age = 22;
            this.score = 88.8;
            System.out.println("实例代码块:比构造方法要先执行,一般用来初始化普通的成员变量");
        }
    
        //静态代码块
        static {
            classes = "0301";
        }
        static {
            classes = "0302";
            System.out.println("打印静态代码块");
        }
    }
    public class TestDemo {
        public static void main(String[] args) {
            System.out.println(Student.classes);
        }
    }
    
    

    image-20220526225555905

9、内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。

9.1 分类

  1. 实例内部类
  2. 静态内部类
  3. 匿名内部类

9.2 实例内部类

用法:

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

    class InnerClass {
        //这里就是实例内部类
    }
}
  1. 如何实例化内部类对象:

    1.外部类名.内部类名 变量 = new 外部类名().new 内部类名(); 
    2.外部类名 变量1 = new 外部类名();
      外部类名.内部类名 变量2 = 变量1.new 内部类名();
    

    举例:

    方式一:

    OuterClass.InnerClass innerclass1 = new OuterClass().new InnerClass();
    

    方式二:

    OuterClass outerclass = new OuterClass();
    OuterClass.InnerClass innerclass2 = outerclass.new InnerClass();
    
  2. 实例内部类当中不能定义静态的成员变量的。(非得要定义,可以这样:定义一个final属性的值,这样的话编译期间就确定值了,public static final int data = 10;)

    class OuterClass {
        public int data1 = 10;
        private int data2 = 20;
        public static int data3 = 30;
    
        class InnerClass {
            //这里就是实例内部类
            public static int data4 = 40;//必须要加final才不报错
            public static final int data5 = 50;
        }
    }
    

    image-20220527100321231

    1. 如果实力内部类当中,创建的变量跟外部类当中的变量重名了,优先使用自己的

      class OuterClass {
          public int data1 = 10;
          private int data2 = 20;
          public static int data3 = 30;
      
          class InnerClass {
              //这里就是实例内部类
              public int data1 = 40;
              private int data2 = 50;
      
              public void display() {
                  System.out.println("data1 = " + data1);
                  System.out.println("data2 = " + data2);
              }
          }
      }
      public class TestDemo1 {
          public static void main(String[] args) {
              OuterClass outerClass = new OuterClass();
              OuterClass.InnerClass innerClass = outerClass.new InnerClass();
              innerClass.display();
          }
      }
      

      image-20220527100730446

      那么我们如果想要获取到外部类的成员变量呢?

      外部类类名.外部类成员变量

      class OuterClass {
          public int data1 = 10;
          private int data2 = 20;
          public static int data3 = 30;
      
          class InnerClass {
              //这里就是实例内部类
              public int data1 = 40;
              private int data2 = 50;
      
              public void display() {
                  System.out.println("内部类当中的data1 = " + this.data1);
                  System.out.println("内部类当中的data2 = " + this.data2);
                  System.out.println("外部类当中的data1 = " + OuterClass.this.data1);
                  System.out.println("外部类当中的data2 = " + OuterClass.this.data2);
              }
          }
      }
      public class TestDemo1 {
          public static void main(String[] args) {
              OuterClass outerClass = new OuterClass();
              OuterClass.InnerClass innerClass = outerClass.new InnerClass();
              innerClass.display();
          }
      }
      

      image-20220527101047796

      注意:我们还使用了this,我们可以注意到:实力内部类当中,不仅有自己的this,也有外部类的this

    2. 不能包含静态的方法

      class OuterClass {
          public int data1 = 10;
          private int data2 = 20;
          public static int data3 = 30;
      
          class InnerClass {
              //这里就是实例内部类
              public int data1 = 40;
              private int data2 = 50;
      
              public static void func() {
                  System.out.println("实例内部类里面不能出现静态方法");
              }
              public void display() {
                  System.out.println("内部类当中的data1 = " + this.data1);
                  System.out.println("内部类当中的data2 = " + this.data2);
                  System.out.println("外部类当中的data1 = " + OuterClass.this.data1);
                  System.out.println("外部类当中的data2 = " + OuterClass.this.data2);
              }
          }
      }
      public class TestDemo1 {
          public static void main(String[] args) {
              OuterClass outerClass = new OuterClass();
              OuterClass.InnerClass innerClass = outerClass.new InnerClass();
              innerClass.display();
          }
      }
      
      

      image-20220527101333035

    3. 在实力内部类里面的普通的成员方法里面,不能定义static final属性的值

      public void display() {
          static final int c = 90;
      }
      

      image-20220527101522099

    4. 匿名对象:一般使用在只用一次的时候,但是每访问一次都要重新new一下

      public static void main(String[] args) {
              //匿名对象:new OuterClass()
              System.out.println(new OuterClass().data1);
              System.out.println(new OuterClass().data1);//每访问一次就需要重新new一下
          }
      

    代码汇总:

    class OuterClass {
        public int data1 = 10;
        private int data2 = 20;
        public static int data3 = 30;
    
        class InnerClass {
            //这里就是实例内部类
            public int data1 = 40;
            private int data2 = 50;
            public static final int data4 = 60;
    
            /*public static void func() {
                System.out.println("实例内部类里面不能出现静态方法");
            }*/
            public void display() {
                //static final int c = 90;
                System.out.println("内部类当中的data1 = " + this.data1);
                System.out.println("内部类当中的data2 = " + this.data2);
                System.out.println("--------------------------");
                System.out.println("外部类当中的data1 = " + OuterClass.this.data1);
                System.out.println("外部类当中的data2 = " + OuterClass.this.data2);
                System.out.println("--------------------------");
                System.out.println("外部类当中的data3 = " + OuterClass.this.data3);
                System.out.println("内部类当中的data4 = " + this.data4);
            }
        }
    }
    public class TestDemo1 {
        public static void main2(String[] args) {
            //匿名对象:new OuterClass()
            System.out.println(new OuterClass().data1);
            System.out.println(new OuterClass().data1);//每访问一次就需要重新new一下
        }
        public static void main(String[] args) {
            OuterClass outerClass = new OuterClass();
            OuterClass.InnerClass innerClass = outerClass.new InnerClass();
            innerClass.display();
        }
    }
    

9.3 静态内部类

用法:

class OuterClass2 {
 public int data1 = 10;
 private int data2 = 20;
 public static int data3 = 30;
 
 static class InnerClass2 {
     
 }
}
  1. 如何实例化静态内部类

    外部类.内部类 变量 = new 外部类.内部类();
    

    举例:

    class OuterClass2 {
        public int data1 = 10;
        private int data2 = 20;
        public static int data3 = 30;
    
        static class InnerClass2 {
    
        }
    }
    public class TestDemo2 {
        public static void main(String[] args) {
            OuterClass2.InnerClass2 innerClass2 = new OuterClass2.InnerClass2();
        }
    }
    
  2. 在静态内部类当中,只能访问外部类当中静态成员变量

    要想访问外部类非静态成员变量,就要提供外部类对象(即new一个外部类对象)

    class OuterClass2 {
        public int data1 = 10;
        private int data2 = 20;
        public static int data3 = 30;
    
        static class InnerClass2 {
            public int data4 = 40;
            private int data5 = 50;
            public static int data6 = 60;
            
            public void show() {
                //这样是访问不了的
                System.out.println(data1);
                System.out.println(data2);
            }
        }
    }
    

    image-20220527105158924

    我们需要先new一个外部类对象,在通过外部类对象去调用

    class OuterClass2 {
        public int data1 = 10;
        private int data2 = 20;
        public static int data3 = 30;
    
        static class InnerClass2 {
            public int data4 = 40;
            private int data5 = 50;
            public static int data6 = 60;
    
            OuterClass2 outer = new OuterClass2();//new一个外部类对象
    
            public void show() {
                System.out.println(outer.data1);//外部类对象.成员变量
                System.out.println(outer.data2);//外部类对象.成员变量
                System.out.println(data3);//外部类当中data3是静态的,可以直接访问
                System.out.println(data4);
                System.out.println(data5);
                System.out.println(data6);
            }
        }
    }
    public class TestDemo2 {
        public static void main(String[] args) {
            OuterClass2.InnerClass2 innerClass2 = new OuterClass2.InnerClass2();
            innerClass2.show();
        }
    }
    

    image-20220527105618541

9.3 匿名内部类

  1. 匿名内部类可以创建变量
  2. 匿名内部类可以写方法
  3. 匿名内部类还可以对被匿名的类里面的方法进行重写

写法:

class Test {

}
public class TestDemo3 {
    public static void main(String[] args) {
        new Test() {

        }.test();
    }
} 

代码:

class Test {
    public int a = 19;

    public void test() {
        System.out.println("test()");
    }
}
public class TestDemo3 {
    public static void main(String[] args) {
        new Test() {
            public int c = 10;//1.匿名内部类可以创建变量
            public void test01() {//2.匿名内部类可以写方法
                System.out.println("test01()");
            }

            @Override//匿名内部类还可以对被匿名的类里面的方法进行重写
            public void test() {
                System.out.println("对test函数进行重写");
            }
        }.test();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加勒比海涛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值