类和对象2
5、对象的构造及其初始化
5.1 如何初始化对象?
我们知道,在之前的Date
这个类中,我们需要调用setDate
这个方法来初始化。其实我们还有好几种方法,我们先给大家介绍一下构造方法
5.2 构造方法
5.2.1 概念
- ⽅法名与类名相同
- 没有返回值
举个栗子:
public Date() {
}
这就是一个最简单的不带参数的构造方法。在创建对象的时候,由编译器自动调用,并且在整个对象的生命周期内只调用一次。
当我们实例化一个对象的时候,必须有以下这两部,但不是一定只有这两步
为对象分配内存
调用合适的构造方法?
这句话其实另有玄机,代表构造方法不一定只有一个
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);
}
}
那么我们要注意:当程序没有构造方法的时候,编译器会默认帮我们生成一个不带参数的构造方法。如果你自己写了构造方法,那么编译器就不会再自动生成一个不带参数的构造方法了,我们可以通过下面的代码进行检验
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();
}
}
我们可以看见,这段代码中,我们自己写了一个带三个参数的构造函数,然后main
方法中却调用了不带参数的构造函数,通过图片我们得知,这报错了,是因为我们自己生成构造函数的话,编译器就不会再帮我们生成不带参数的构造函数,所以在主函数调用不带参数的构造函数会报错
5.2.2 特性
-
名字必须与类名相同
-
没有返回值类型,设置为void也不行
-
创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
-
构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)
重载:方法名相同,参数裂变不同,和返回值没有关系
-
如果用户没有自己定义构造函数,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的。(所以一个类是一定至少含有一个构造函数)
-
编译器帮我们实现的构造函数:右键->
generate
->constructer
->选中要操作的属性编译器帮我们实现
show
函数:右键->generate
->toString()
-
构造方法中,可以通过
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); } }
注意:
-
this
必须放在构造函数的第一行public Student() { System.out.println("this只能放在第一行"); this("剪秋",22,98,"女");//调用Student类中带4个参数的构造函数 }
-
一个构造函数里面只能有一个
this
public Student() { this("剪秋",22,98,"女");//调用Student类中带4个参数的构造函数 this("宝娟",25,77); }
-
不能成环
//不带参数的构造函数 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; }
-
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();
}
}
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);
}
}
我们可以看到,类里面的属性被设置成了私有,但是我们还生成了一系列getter
,setter
方法来给类的使用者去获取到相关信息,所以这就是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
那么现在,在主函数我们想要直接访问name
,age
,color
方法是访问不到的,只能通过getter
,setter
方法
我们接下来继续看,如果我们传入的对象是空的时候,是什么效果?
public static void main(String[] args) {
Cat cat = null;
System.out.println(cat);//传过去null,打印的就是字符串null
}
我们看运行结果,是null
我们在输入数据的时候,同样可以采用构造方法
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();
}
但是奇怪的是,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());
}
}
那么我们需要显示指定才能解决,像下面这样
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++
的 namespace
和 using
6.3.2 自定义包
⼀般全部小写,公司域名逆序
-
基本规则
-
在文件的最上方加上一个
package
语句指定该代码在哪个包中. -
包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如
com.baidu.demo
) -
包名要和代码路径相匹配. 例如创建
com.baidu.demo
的包, 那么会存在一个对应的路径com/baidu/demo
来存储
代码. -
如果一个类没有
package
语句, 则该类被放到一个默认包中
-
-
创建方法
-
src
->右键->New
->Package
-
公司域名逆序,全部小写,中间用
.
分隔 -
这时候我们就可以看见文件夹创建了
-
6.3.3 访问限定符
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);//默认权限 只能在同一个包下访问
}
}
6.3.5 常见的包
java.lang
:系统常用基础类(String,object
)java.lang.reflect
:Java
反射基础包java.net
:网络编程开发包java.sql
:数据库开发支持包java.util
:工具程序包(集合类等),非常重要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
修饰的成员变量,称为静态成员变量。
它最大的特性就是:不属于某个具体的对象,是所有对象所共享的。
那么他的特性都有:
-
不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
-
既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问,而且这样也不需要
new
对象了Student.class;//推荐 student.class;//不推荐
-
JDK7
及以前,HotSpot
(Java
虚拟机)中存储在方法区,JDK8
及之后,类变量存储在Java
堆中其实方法区和堆逻辑上都属于同一个等级的关系,只不过在
JVM
当中实现这个东西的时候,把他放在了堆里 -
类变量存储在方法区当中
-
生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
类在的话就在,不在就没了
举个栗子吧:
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);
}
}
那么我们可以调试一下,看看静态的成员变量是否存在于某个具体的变量当中
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);
}
}
我们可以通过静态的成员方法,来实现在类外访问被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());
}
}
静态方法的特性:
-
不属于某个具体的对象,是类方法
-
可以通过对象调用,也可以通过类名.静态方法名(…)方式调用,更推荐使用后者
Student.getClassRoom(); student.getClassRoom();
-
静态方法没有隐藏的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()); } }
-
静态方法中不能调用任何非静态方法,因为非静态方法有
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()); } }
-
通过对象.成员变量可能会产生问题
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);
}
}
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);
}
}
静态代码块的注意事项:
-
静态代码块也是不依赖对象的,里面不能出现非静态成员
//静态代码块 static { this("富察贵人",78,11);//静态代码块里面不能出现this age = age + 1;//静态代码块里面不能出现非静态成员变量 classes = "0301"; System.out.println("静态代码块:最先执行,一般用来初始化静态成员变量"); }
-
出现两个静态代码块的时候,打印的结果是最后一个
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); } }
-
在普通的成员方法里面是可以使用静态的成员变量的
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(); } }
-
静态的成员方法中,不能访问非静态的成员
public static void func() { //在静态成员函数里面不能访问非静态成员 doclass(); System.out.println(age); //在静态成员函数里面访问静态局部变量可以 System.out.println(classes); }
-
静态代码块先执行,并且只执行一次,在类加载阶段执行
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); } }
-
实例代码块只有在创建对象的时候执行
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); } }
9、内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。在 Java
中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
9.1 分类
- 实例内部类
- 静态内部类
- 匿名内部类
9.2 实例内部类
用法:
class OuterClass {
public int data1 = 10;
private int data2 = 20;
public static int data3 = 30;
class InnerClass {
//这里就是实例内部类
}
}
-
如何实例化内部类对象:
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();
-
实例内部类当中不能定义静态的成员变量的。(非得要定义,可以这样:定义一个
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; } }
-
如果实力内部类当中,创建的变量跟外部类当中的变量重名了,优先使用自己的
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(); } }
那么我们如果想要获取到外部类的成员变量呢?
外部类类名.外部类成员变量
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(); } }
注意:我们还使用了
this
,我们可以注意到:实力内部类当中,不仅有自己的this
,也有外部类的this
-
不能包含静态的方法
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(); } }
-
在实力内部类里面的普通的成员方法里面,不能定义
static final
属性的值public void display() { static final int c = 90; }
-
匿名对象:一般使用在只用一次的时候,但是每访问一次都要重新
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 {
}
}
-
如何实例化静态内部类
外部类.内部类 变量 = 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(); } }
-
在静态内部类当中,只能访问外部类当中静态成员变量
要想访问外部类非静态成员变量,就要提供外部类对象(即
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); } } }
我们需要先
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(); } }
9.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();
}
}