0%

javaSE基础学习day14

abstract关键字

随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。 abstract关键字可以用来修饰的结构:类、方法。

abstract修饰类:抽象类

  • 此类不能实例化。
  • 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)。
  • 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作。

abstract修饰方法:抽象方法

  • 抽象方法只有方法的声明,没有方法体。
  • 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
  • 若子类重写了父类中的所有的抽象方法后,此子类方可实例化
  • 若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰。

❗ 不能用abstract修饰变量、代码块、构造器;
❗ 不能用abstract修饰私有方法、静态方法、final的方法、final的类。

抽象类的应用

抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类。

在航运公司系统中,Vehicle类需要定义两个方法分别计算运输工具的燃料效率和行驶距离。

  • 问题:卡车(Truck)和驳船(RiverBarge)的燃料效率和行驶距离的计算方法完全不同。Vehicle类不能提供计算方法,但子类可以。

  • 解决方案:Java允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由子类提供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。
  • Vehicle是一个抽象类,有两个抽象方法。
1
2
3
4
5
6
7
8
9
10
11
12
public abstract class Vehicle{
public abstract double calcFuelEfficiency(); //计算燃料效率的抽象方法
public abstract double calcTripDistance(); //计算行驶距离的抽象方法
}
public class Truck extends Vehicle{
public double calcFuelEfficiency(){ //写出计算卡车的燃料效率的具体方法 }
public double calcTripDistance(){ //写出计算卡车行驶距离的具体方法 }
}
public class RiverBarge extends Vehicle{
public double calcFuelEfficiency(){ //写出计算驳船的燃料效率的具体方法 }
public double calcTripDistance(){ //写出计算驳船行驶距离的具体方法}
}

❗ 注意:抽象类不能实例化 new Vihicle()是非法的

练习1

编写一个Employee类,声明为抽象类,包含如下三个属性:name,id,salary。提供必要的构造器和抽象方法:work()。对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。请使用继承的思想,设计CommonEmployee类和Manager类,要求类中提供必要的方法进行属性访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class Employee{
private String name;
private int id;
private double salary;
public Employee(){
}
public Employee(String name;int id;double salary){
this.name = name;
this.id = id;
this.salary = salary;
}
public abstract void work();
}
1
2
3
4
5
6
7
8
9
10
11
public class Manager extends Employee{
private double bonus;
public Manager(String name;int id;double salary,double bonus){
super(name,salary,bonus);
this.bonus = bonus;
}
@Override
public void work(){
System.out.println("Manager工作的具体方法")
}
}
1
2
3
4
5
6
public class CommonEmployee extends Employee {
@Override
public void work() {
System.out.println("员工在一线车间生产产品");
}
}
1
2
3
4
5
6
7
8
9
10
public class EmployeeTest {
public static void main(String[] args) {
//多态
Employee manager = new Manager("库克", 1001, 5000, 50000);
manager.work();

CommonEmployee commonEmployee = new CommonEmployee();
commonEmployee.work();
}
}

抽象类的匿名子类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class PersonTest {
public static void main(String[] args) {
Worker worker = new Worker();
method1(worker);//非匿名的类非匿名的对象

method1(new Worker());//非匿名的类匿名的对象

//创建了一匿名子类的对象:p
Person p = new Person() {
@Override
public void eat() {
System.out.println("吃东西");
}
@Override
public void breath() {
System.out.println("好好呼吸");
}
};
method1(p);// 吃东西/n好好呼吸
//创建匿名子类的匿名对象
method1(new Person() {
@Override
public void eat() {
System.out.println("吃好吃东西");
}
@Override
public void breath() {
System.out.println("好好呼吸新鲜空气");
}
});
}
public static void method1(Person p) {
p.eat();
p.breath();
}
public static void method(Student s) {
}
}

class Worker extends Person {
@Override
public void eat() {}
@Override
public void breath() {}
}

模板方法设计模式(TemplateMethod)

抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。

解决的问题:

  • 当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
  • 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class TemplateTest {
public static void main(String[] args) {
SubTemplate t = new SubTemplate();
t.spendTime();
}
}
abstract class Template{
//计算某段代码执行所需要花费的时间
public void spendTime(){
long start = System.currentTimeMillis();
this.code();//不确定的部分、易变的部分
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
}
public abstract void code();
}
class SubTemplate extends Template{
@Override
public void code() {
for(int i = 2;i <= 1000;i++){
boolean isFlag = true;
for(int j = 2;j <= Math.sqrt(i);j++){
if(i % j == 0){
isFlag = false;
break;
}
}
if(isFlag){
System.out.println(i);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class TemplateMethodTest {
public static void main(String[] args) {
BankTemplateMethod btm = new DrawMoney();
btm.process();
BankTemplateMethod btm2 = new ManageMoney();
btm2.process();
}
}
abstract class BankTemplateMethod {
// 具体方法
public void takeNumber() {
System.out.println("取号排队");
}
public abstract void transact(); // 办理具体的业务 //钩子方法
public void evaluate() {
System.out.println("反馈评分");
}
// 模板方法,把基本操作组合到一起,子类一般不能重写
public final void process() {
this.takeNumber();
this.transact();// 像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码
this.evaluate();
}
}
class DrawMoney extends BankTemplateMethod {
public void transact() {
System.out.println("我要取款!!!");
}
}
class ManageMoney extends BankTemplateMethod {
public void transact() {
System.out.println("我要理财!我这里有2000万美元!!");
}
}

模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的影子,比如常见的有:
💠 数据库访问的封装
💠 Junit单元测试
💠 JavaWeb的Servlet中关于doGet/doPost方法调用
💠 Hibernate中模板程序
💠 Spring中JDBCTemlate、HibernateTemplate等

练习2

编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个Employee对象的生日,则将该雇员的工资增加100元。实验说明:
(1)定义一个Employee类,该类包含:
private成员变量name,number,birthday;
其中birthday 为MyDate类的对象;abstract方法earnings();
toString()方法输出对象的name,number和birthday。
(2)MyDate类包含:
private成员变量year,month,day ;
toDateString()方法返回日期对应的字符串:xxxx年xx月xx日。
(3)定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。该类包括:
private成员变量monthlySalary;
实现父类的抽象方法earnings(),该方法返回monthlySalary值;
toString()方法输出员工类型信息及员工的name,number,birthday。
(4)定义HourlyEmployee类继承Employee类,实现按小时计算工资的员工处理。该类包括:
private成员变量wage和hour;
实现父类的抽象方法earnings(),该方法返回wage*hour值;
toString()方法输出员工类型信息及员工的name,number,birthday。
(5)定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。
利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday。
当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//定义一个Employee类,该类包含:private成员变量name,number,birthday;其中birthday 为MyDate类的对象;abstract方法earnings();toString()方法输出对象的name,number和birthday。
public abstract class Employee{
private String name;
private int number;
private MyDate birthday;
public Employee(String name,int number,MyDate birthday){
this.name = name;
this.number = number;
this.birthday = birthday;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setNumber(int number){
this.number = number;
}
public int getNumber(){
return number;
}
public void setBirthday(MyDate birthday){
this.birthday = birthday;
}
public MyDate getBirthday(){
return birthday;
}
public abstract double earnings();
public String toString(){
return "name=" + name + ",number=" + number + ",birthday=" + birthday.toDateString();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//MyDate类包含:private成员变量year,month,day ;toDateString()方法返回日期对应的字符串:xxxx年xx月xx日。
public class MyDate{
private int year;
private int month;
private int day;
public MyDate(int year,int month,int day){
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public int getMonth() {
return month;
}
public int getDay() {
return day;
}
public void setYear(int year) {
this.year = year;
}
public void setMonth(int month) {
this.month = month;
}
public void setDay(int day) {
this.day = day;
}
public String toDateString(){
return year + "年" + month + "月" + day + "日";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。该类包括:private成员变量monthlySalary;实现父类的抽象方法earnings(),该方法返回monthlySalary值;toString()方法输出员工类型信息及员工的name,number,birthday。
public class SalariedEmployee extends Employee{
private double monthlySalary;
public SalariedEmployee(String name,int number,MyDate birthday){
super(name,number,birthday);
}
public SalariedEmployee(String name,int number,MyDate birthday,double monthlySalary){
super(name,number,birthday);
this.monthlySalary = monthlySalary;
}
public double getMonthlySalary() {
return monthlySalary;
}
public void setMonthlySalary(double monthlySalary) {
this.monthlySalary = monthlySalary;
}
@Override
public double earnings(){
return monthlySalary;
}
@Override
public String toString(){
return "SalariedEmployee[" + super.toString() + "]";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//定义HourlyEmployee类继承Employee类,实现按小时计算工资的员工处理。该类包括:private成员变量wage和hour;实现父类的抽象方法earnings(),该方法返回wage*hour值;toString()方法输出员工类型信息及员工的name,number,birthday。
public class HourlyEmployee extends Employee{
private double wage;
private int hour;
public HourlyEmployee(String name,int number,MyDate birthday){
super(name,number,birthday);
}
public HourlyEmployee(String name,int number,MyDate birthday,double wage,int hour){
super(name,number,birthday);
this.wage = wage;
this.hour = hour;
}
@Override
public double earnings(){
return wage * hour;
}
@Override
public String toString(){
return "HourlyEmployee[" + super.toString() + "]";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday。当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。
import java.utils.Calendar;
public class PayrollSystem{
public static void main(String[] args){
Calendar calendar = Calendar.getInstance();
int month = calendar.get(Calendar.MONTH);//获取当前的月份 一月份:0

Employee[] employee = new Employee[2];
employee[0] = new SalariedEmployee("张博",1001,new MyDate(1996,9,3),10000);
employee[1] = new HourlyEmployee("潘虹",1002,new MyDate(1997,4,25),60,240);

for(int i = 0;i < employee.length;i++){
System.out.println(employee[i].toString());

double salary = employee[i].earnings();
if(employee[i].getBirthday().getMonth() == (month+1){
salary = salary + 100;
System.out.println("生日快乐!奖励100元!");
}
System.out.println("月工资为:" + salary);
}
}
}

接口(interface)

  • 一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
  • 另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。
  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个”是不是”的关系,而接口实现则是 “能不能”的关系。
  • 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。

接口使用interface来定义。在Java中,接口和类是并列关系,可以理解为一种特殊的抽象类。接口只包含全局常量和抽象方法的定义(JDK7.0及之前,JDK8除了定义全局常量和抽象方法之外,还可以定义静态方法和默认方法),而没有变量和方法的实现。

接口中的所有成员变量都默认是由public static final修饰的。但是书写时,可以省略不写。
接口中的所有抽象方法都默认是由public abstract修饰的。但是书写时,可以省略不写。

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Runner {
int ID = 1;
void start();
public void run();
void stop();
}
//相当于
public interface Runner {
public static final int ID = 1;
public abstract void start();
public abstract void run();
public abstract void stop();
}

接口中不能定义构造器的!意味着接口不可以实例化。

Java开发中,接口通过让类去实现(implements)的方式来使用。(面向接口编程)

  • 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化。
  • 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类

Java类可以实现多个接口,弥补了Java单继承性的局限性。(先写extends,后写implements)

1
class AA extends BB implements CC,DD,EE{}  //格式

接口与接口之间可以继承,而且可以多继承。

1
interface CC extends AA,BB{}

接口的具体使用,体现多态性。接口,实际上可以看做是一种规范。

项目的具体需求是多变的,我们必须以不变应万变才能从容开发,此处的“不变”就是“规范”。因此,我们开发项目往往都是面向接口编程。

接口的应用:代理模式(Proxy)

代理模式是Java开发中使用较多的一种设计模式。
代理设计就是为其他对象提供一种代理以控制对这个对象的访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
interface Network {
public void browse();
}
// 被代理类
class RealServer implements Network {
@Override
public void browse() {
System.out.println("真实服务器上网浏览信息");
}
}
// 代理类
class ProxyServer implements Network {
private Network network;
public ProxyServer(Network network) {
this.network = network;
}
public void check() {
System.out.println("检查网络连接等操作");
}
public void browse() {
check();
network.browse();
}
}
public class ProxyDemo {
public static void main(String[] args) {
Network net = new ProxyServer(new RealServer());
net.browse();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class StaticProxyTest {
public static void main(String[] args) {
Proxy s = new Proxy(new RealStar());
s.confer();
s.signContract();
s.bookTicket();
s.sing();
s.collectMoney();
}
}
interface Star {
void confer();// 面谈
void signContract();// 签合同
void bookTicket();// 订票
void sing();// 唱歌
void collectMoney();// 收钱
}
//被代理类
class RealStar implements Star {
public void confer() {
}
public void signContract() {
}
public void bookTicket() {
}
public void sing() {
System.out.println("明星:歌唱~~~");
}
public void collectMoney() {
}
}
//代理类
class Proxy implements Star {
private Star real;
public Proxy(Star real) {
this.real = real;
}
public void confer() {
System.out.println("经纪人面谈");
}
public void signContract() {
System.out.println("经纪人签合同");
}
public void bookTicket() {
System.out.println("经纪人订票");
}
public void sing() {
real.sing();
}
public void collectMoney() {
System.out.println("经纪人收钱");
}
}

代理模式的应用场景:
🔹 安全代理:屏蔽对真实角色的直接访问。
🔹 远程代理:通过代理类处理远程方法调用(RMI)。
🔹 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。

比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式。当需要查看图片时,用proxy来进行大图片的打开。

代理模式的分类:
🔹 静态代理(静态定义代理类)
🔹 动态代理(动态生成代理类): JDK自带的动态代理,需要反射等知识。

面试题

排错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface A {
int x = 0;
}
class B {
int x = 1;
}
class C extends B implements A {
public void pX() {
//System.out.println(x);//编译不通过<The field x is ambiguous> 属性x是不明确的
System.out.println(super.x);//1
System.out.println(A.x);//0
}
public static void main(String[] args) {
new C().pX();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface Playable {
void play();
}
interface Bounceable {
void play();
}
interface Rollable extends Playable,Bounceable {
Ball ball = new Ball("PingPang");//省略了public static final修饰符
}
class Ball implements Rollable {
private String name;
public String getName() {
return name;
}
public Ball(String name) {
this.name = name;
}
public void play() {
ball = new Ball("Football");//编译不通过<The final field Rollable.ball cannot be assignedball> ball是全局常量,不可重新赋值
System.out.println(ball.getName());
}
}

练习

定义一个接口用来实现两个对象的比较。
定义一个Circle类,声明redius属性,提供getter和setter方法。
定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。
定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小。

1
2
3
public interface CompareObject{
public int compareTo(Object o); //若返回值是0, 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
}
1
2
3
4
5
6
7
8
9
10
11
12
public class Circle{
private Double radius;
public Circle(Double radius){
this.radius = radius;
}
public void setRadius(Double radius){
this.radius = radius;
}
public Double getRadius(){
return radius;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ComparableCircle extends Circle implements CompareObject{
public ComparableCircle(Double radius){
super(radius);
}
public int compareTo(Object o){
if(o = this){
return 0;
}
if(o instanceof ComparableCircle){
ComparableCircle other = (ComparableCircle) o;
return this.getRadius().CompareTo(other.getRadius());
}else{
throw new RuntimeException("传入的数据类型不匹配");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class InterfaceTest{
ComparableCircle c1 = new ComparableCircle(3.4);
ComparableCircle c1 = new ComparableCircle(3.6);
int compareValue = c1.compareTo(c2);
if(compareValue > 0){
System.out.println("c1对象大");
}else if(compareValue < 0){
System.out.println("c2对象大");//输出
}else{
System.out.println("c1与c2一样大");
}
int compareValue1 = c1.compareTo(new String("AA"));
System.out.println(compareValue1);//java.lang.RuntimeException: 传入的数据类型不匹配
}

Java 8中关于接口的改进

Java 8中,你可以为接口添加静态方法默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。

静态方法:使用 static 关键字修饰。只能通过接口调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。

默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法。

  • 如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法。
  • 如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,会出现:接口冲突。这就需要在实现类中重写接口中同名同参数的方法。
  • 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。因为此时遵守:类优先原则
  • 在子类(或实现类)的方法中调用父类、接口中被重写的方法:
1
2
3
method();//调用自己定义的重写的方法
super.method();//调用的是父类中声明的方法
InterfaceTest.super.method();//调用接口中的默认方法

内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。

在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类

Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
Inner class的名字不能与包含它的外部类类名相同。

内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)

成员内部类:

一方面,作为外部类的成员:

  • 调用外部类的结构;
  • 可以被static修饰,但此时就不能再使用外部类的非static的成员变量;
  • 可以被4种不同的权限修饰,和外部类不同,Inner class还可以声明为private或protected

另一方面,作为一个类:

  • 类内可以定义属性、方法、构造器等结构;
  • 可以被abstract修饰,因此可以被其它的内部类继承;
  • 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承。
  • 编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Outer {
private int s = 111;
public class Inner {
private int s = 222;
public void mb(int s) {
System.out.println(s); // 局部变量s
System.out.println(this.s); // 内部类对象的属性s
System.out.println(Outer.this.s); // 外部类对象属性s
}
}
public static void main(String args[]) {
Outer a = new Outer();
Outer.Inner b = a.new Inner();
b.mb(333);
}
}
  1. 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员。
  2. 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
  3. 成员内部类可以直接使用外部类的所有成员,包括私有的数据
  4. 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的

局部内部类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class InnerClassTest {
public void method(){
//局部内部类
class AA{
}
}

{
//局部内部类
class BB{
}
}

public Person(){
//局部内部类
class CC{
}
}
}
  • 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号,以及数字编号。
  • 只能在声明它的方法或代码块中使用,而且是先声明后使用。其他任何地方都不能使用该类。
  • 局部内部类可以使用外部类的成员,包括私有的。
  • 局部内部类可以使用外部方法的局部变量,但是必须是final的。这是由局部内部类和局部变量的声明周期不同所致。
  • 局部内部类和局部变量地位类似,不能使用public,protected,缺省,private。
  • 局部内部类不能使用static修饰,因此也不能包含静态成员。
  • 本文作者: susin3x
  • 本文链接: https://susin3x.cn/posts/b7afdeae.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!