面向对象思想引入
前面我们讲过数组,当有多个数组都需要遍历时,我们可以将遍历的代码封装到方法中,需要遍历时,就调用相应的方法即可,提高代码的复用性。在对数组遍历的基础上继续增加需求,比如获取最值,数值逆序等,同样需要将这些功能封装到相应的方法中。这样继续封装会发现方法越来越多,于是就想能不能将这些方法继续进行封装呢?通过前面的讲解我们知道类是可以存放方法的,所以,我们就考虑使用类封装来这多个方法,将来再做数组的操作时,不用去找具体的方法,先找到这个类,然后使用这个类中的方法。这就是面向对象思想的编程方式。
面向过程思想概述
- 我们来回想一下,我们完成一个需求的步骤:首先是搞清楚我们要做什么,然后在分析怎么做,最后我们再代码体现。一步一步去实现,而具体的每一步都需要我们去实现和操作。这些步骤相互调用和协作,完成我们的需求。
- 在上面的每一个具体步骤中我们都是参与者,并且需要面对具体的每一个步骤和过程,这就是面向过程最直接的体现。
- 那么什么是面向过程开发呢?
面向过程开发,其实就是面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。 - 面向过程的代表语言:C语言
- 当需求单一,或者简单时,我们一步一步去操作没问题,并且效率也挺高。可随着需求的更改,功能的增多,发现需要面对每一个步骤很麻烦了,这时就开始思索,能不能把这些步骤和功能在进行封装,封装时根据不同的功能,进行不同的封装,功能类似的封装在一起。这样结构就清晰了很多。用的时候,找到对应的类就可以了。这就是面向对象的思想。接下来我们看看面向对象到底是什么?
面向对象思想概述
面向对象是基于面向过程的编程思想
面向对象思想特点
是一种更符合我们思想习惯的思想
可以将复杂的事情简单化
将我们从执行者变成了指挥者
角色发生了转换
面向对象开发,设计,特征
面向对象开发
就是不断的创建对象,使用对象,指挥对象做事情。
面向对象设计
其实就是在管理和维护对象之间的关系。
面向对象特征
封装(encapsulation)
继承(inheritance)
多态(polymorphism)
类与对象关系
我们学习编程语言,就是为了模拟现实世界的事物,实现信息化。比如:去超市买东西的计费系统,去银行办业务的系统。
我们如何表示一个现实世界事物呢:
属性 就是该事物的描述信息
行为 就是该事物能够做什么
举例:学生事物我们学习的Java语言最基本单位是类,所以,我们就应该把事物用一个类来体现。
类:是一组相关的属性和行为的集合
对象:是该类事物的具体体现
举例:
类 学生
对象 班长就是一个对象
类:可以理解为构造对象的一个蓝图或者模版,是抽象的概念
对象:是以类为模型创建的具体实例,是对类的一种具体化。
类与对象(图例)
类与对象的关系如图
1:图纸就是类
2:每一个汽车就是一个个的对象
类的定义
现实世界的事物
属性 人的身高,体重等
行为 人可以学习,吃饭等Java中用class描述事物也是如此 成员变量 就是事物的属性
成员方法 就是事物的行为
定义类其实就是定义类的成员(成员变量和成员方法)
成员变量和局部变量的区别
在类中的位置不同
成员变量 类中方法外
局部变量 方法内或者方法声明上在内存中的位置不同
成员变量 堆内存
局部变量 栈内存生命周期不同
成员变量 随着对象的存在而存在,随着对象的消失而消失
局部变量 随着方法的调用而存在,随着方法的调用完毕而消失初始化值不同
成员变量 有默认的初始化值
局部变量 没有默认的初始化值,必须先定义,赋值,才能使用。
形式参数问题
基本类型作为形式参数
引用类型作为形式参数
匿名对象
匿名对象:就是没有名字的对象。
是对象的一种简化表示形式
匿名对象的两种使用情况
对象调用方法仅仅一次的时候
作为实际参数传递
封装概述
封装概述
是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
好处:
隐藏实现细节,提供公共的访问方式
提高了代码的复用性
提高安全性。封装原则:
将不需要对外提供的内容都隐藏起来。
把属性隐藏,提供公共方法对其访问。
private关键字
private关键字:
是一个权限修饰符。
可以修饰成员(成员变量和成员方法)
被private修饰的成员只在本类中才能访问。private最常见的应用:
把成员变量用private修饰
提供对应的getXxx()/setXxx()方法
this关键字
- this:代表所在类的对象引用
记住:
方法被哪个对象调用,this就代表那个对象
什么时候使用this呢?
局部变量隐藏成员变量
其他用法后面和super一起讲解
构造方法
构造方法作用概述
给对象的数据进行初始化
构造方法格式
方法名与类名相同
没有返回值类型,连void都没有
没有具体的返回值构造方法注意事项
如果你不提供构造方法,系统会给出默认构造方法
如果你提供了构造方法,系统将不再提供
构造方法也是可以重载的
类的成员方法
成员方法其实就是我们前面讲过的方法
方法具体划分:
根据返回值
有明确返回值方法
返回void类型的方法根据形式参数
无参方法
带参方法
一个基本类的标准代码写法
类
成员变量
构造方法
无参构造方法
带参构造方法成员方法
getXxx()
setXxx()给成员变量赋值的方式
无参构造方法+setXxx()
带参构造方法
类的初始化过程
Student s = new Student();在内存中做了哪些事情?
1.加载Student.class文件进内存
2.在栈内存为s开辟空间
3.在堆内存为学生对象开辟空间
4.对学生对象的成员变量进行默认初始化
5.对学生对象的成员变量进行显示初始化
6.通过构造方法对学生对象的成员变量赋值
7.学生对象初始化完毕,把对象地址赋值给s变量
static关键字
- 可以修饰成员变量和成员方法
static关键字特点
随着类的加载而加载
优先于对象存在
被类的所有对象共享(这也是我们判断是否使用静态关键字的条件)
可以通过类名调用static关键字注意事项
在静态方法中是没有this关键字的
静态方法只能访问静态的成员变量和静态的成员方法
静态变量和成员变量的区别
所属不同
静态变量属于类,所以也称为为类变量
成员变量属于对象,所以也称为实例变量(对象变量)内存中位置不同
静态变量存储于方法区的静态区
成员变量存储于堆内存内存出现时间不同
静态变量随着类的加载而加载,随着类的消失而消失
成员变量随着对象的创建而存在,随着对象的消失而消失调用不同
静态变量可以通过类名调用,也可以通过对象调用
成员变量只能通过对象名调用
main方法是静态的
public static void main(String[] args) {}
public 被jvm调用,访问权限足够大。
static 被jvm调用,不用创建对象,直接类名访问
void被jvm调用,不需要给jvm返回值
main 一个通用的名称,虽然不是关键字,但是被jvm识别
String[] args 以前用于接收键盘录入的
代码块
- 在Java中,使用{}括起来的代码被称为代码块,根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块。
局部代码块
在方法中出现;限定变量生命周期,及早释放,提高内存利用率
构造代码块
在类中方法外出现;多个构造方法方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行
静态代码块 在类中方法外出现,加了static修饰
在类中方法外出现,并加上static修饰;用于给类进行初始化,在加载的时候就执行,并且值执行一次。
继承概述
继承概述
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
通过extends关键字可以实现类与类的继承
class 子类名 extends 父类名 {}
单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。
有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。
继承的好处
提高了代码的复用性
多个类相同的成员可以放到同一个类中
提高了代码的维护性
如果功能的代码需要修改,修改一处即可
让类与类之间产生了关系,是多态的前提
其实这也是继承的一个弊端:类的耦合性很强
Java只支持单继承,不支持多继承。
一个类只能有一个父类,不可以有多个父类。
class SubDemo extends Demo{} //ok
class SubDemo extends Demo1,Demo2...//error
Java支持多层继承(继承体系)
class A{}
class B extends A{}
class C extends B{}
Java中继承的注意事项
子类只能继承父类所有非私有的成员(成员方法和成员变量)
其实这也体现了继承的另一个弊端:打破了封装性
子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。
不要为了部分功能而去继承
我们到底在什么时候使用继承呢?
继承中类之间体现的是:”is a”的关系。
继承中成员变量的关系
1:在父类中定义一个成员变量
2:在子类中定义一个成员变量和父类中成员变量名称不同,然后再在子类中定义一个方法去访问变量,发现变量名不同,访问非常简单
3:在子类中再定义一个成员变量,和父类中的成员变量名称一致,然后继续访问。发现访问的是子类的成员变量。
4:如果我要访问父类的成员变量该怎么办呢?通过回想this来引入super关键字
结论:
在子类方法中访问一个变量
首先在子类局部范围找
然后在子类成员范围找
最后在父类成员范围找(肯定不能访问到父类局部范围)
如果还是没有就报错。(不考虑父亲的父亲…)
super关键字
- super的用法和this很像
- this代表本类对应的引用。
- super代表父类存储空间的标识(可以理解为父类引用)
用法(this和super均可如下使用)
访问成员变量
this.成员变量 super.成员变量
访问构造方法(子父类的构造方法问题讲)
this(…) super(…)
访问成员方法(子父类的成员方法问题讲)
this.成员方法() super.成员方法()
1:看程序写结果
class Test
{
private static int x = 10;
public void show(int x)
{
x++;
System.out.println(x);
}
public static void main(String[] args)
{
int x = 20;
Test t = new Test();
t.show(x);
}
}
结果:21
继承中构造方法的关系
子类中所有的构造方法默认都会访问父类中空参数的构造方法
为什么呢?
因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化。
每一个构造方法的第一条语句默认都是:super()如何父类中没有构造方法,该怎么办呢?
子类通过super去显示调用父类其他的带参的构造方法
子类通过this去调用本类的其他构造方法
本类其他构造也必须首先访问了父类构造一定要注意:
super(…)或者this(….)必须出现在第一条语句山
否则,就会有父类数据的多次初始化
1:看程序写结果
class Fu{
public int num = 10;
public Fu(){
System.out.println("fu");
}
}
class Zi extends Fu{
public int num = 20;
public Zi(){
System.out.println("zi");
}
public void show(){
int num = 30;
System.out.println(num);
System.out.println(this.num);
System.out.println(super.num);
}
}
class Test {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
2:面试题
class Fu {
static {
System.out.println("静态代码块Fu");
}
{
System.out.println("构造代码块Fu");
}
public Fu() {
System.out.println("构造方法Fu");
}
}
class Zi extends Fu {
static {
System.out.println("静态代码块Zi");
}
{
System.out.println("构造代码块Zi");
}
public Zi() {
System.out.println("构造方法Zi");
}
}
Zi z = new Zi(); 请执行结果。
A:静态随着类的加载而加载。
B:静态代码块 -- 构造代码块 -- 构造方法的执行流程
静态代码块 -- 构造代码块 -- 构造方法
C:只要有子父关系,肯定先初始化父亲的数据,然后初始化子类的数据。
结果:
静态代码块Fu
静态代码块Zi
构造代码块Fu
构造方法Fu
构造代码块Zi
构造方法Zi
3:面试题
class X {
Y b = new Y();
X() {
System.out.print("X");
}
}
class Y {
Y() {
System.out.print("Y");
}
}
public class Z extends X {
Y y = new Y();
Z() {
System.out.print("Z");
}
public static void main(String[] args) {
new Z();
}
}
结果:
【铺垫的小知识】:
第一个:成员变量有基本类型和引用类型的。
class Demo { //基本类型 int x = 10; //引用类型 Student s = new Student(); }
第二个:类的初始化过程
加载class文件
堆中开辟空间
变量的默认初始化
变量的显示初始化
构造代码块初始化
构造方法初始化第三个:遇到extends,就要知道,先初始化父类数据,然后初始化子类数据。
分层初始化。
super在这里仅仅表示要先初始化父类数据。
继承中成员方法的关系
1:在父类中定义一个成员方法
2:在子类中定义一个成员方法和父类中成员方法名称不同,然后在测试类中通过子类对象去访问方法,发现方法名不同,访问非常简单
3:在子类中再定义一个成员方法,和父类中的成员方法名称一致,然后继续访问。发现访问的是子类的成员方法。
4:如果我要访问父类的成员方法该怎么办呢?回想刚才提过的super关键字
结论:
通过子类对象去访问一个方法
首先在子类中找
然后在父类中找
如果还是没有就报错。(不考虑父亲的父亲…)方法重写概述
子类中出现了和父类中一模一样的方法声明,也被称为方法覆盖,方法复写。
使用特点:
如果方法名不同,就调用对应的方法
如果方法名相同,最终使用的是子类自己的方法重写的应用:
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
方法重写的注意事项
父类中私有方法不能被重写
子类重写父类方法时,访问权限不能更低
父类静态方法,子类也必须通过静态方法进行重写。
final关键字
final关键字是最终的意思,可以修饰类,成员变量,成员方法。
修饰类,类不能被继承
修饰变量,变量就变成了常量,只能被赋值一次
修饰方法,方法不能被重写final关键字面试题
final修饰局部变量
在方法内部,该变量不可以被改变
在方法声明上,分别演示基本类型和引用类型作为参数的情况
1.基本类型,是值不能被改变
2.引用类型,是地址值不能被改变final修饰变量的初始化时机
在对象构造完毕前即可
多态概述
多态概述
某一个事物,在不同时刻表现出来的不同状态。
举例:
猫可以是猫的类型。猫 m = new 猫();
同时猫也是动物的一种,也可以把猫称为动物。
动物 d = new 猫();
在举一个例子:水在不同时刻的状态多态前提和体现
有继承关系
有方法重写
有父类引用指向子类对象
多态案例及成员访问特点
成员访问特点
成员变量
编译看左边,运行看左边成员方法
编译看左边,运行看右边
静态方法
编译看左边,运行看左边
所以前面我说静态方法不能算方法的重写
面试题:
1:看程序写结果(先判断有没有问题,如果没有,写出结果)
class Fu
{
public void show()
{
System.out.println("fu show");
}
}
class Zi extends Fu
{
public void show()
{
System.out.println("zi show");
}
public void method()
{
System.out.println("zi method");
}
}
class Test
{
public static void main(String[] args)
{
Fu f = new Zi();
f.method();
}
}
2.看程序写结果
class Fu
{
public void show()
{
System.out.println("fu show");
}
}
class Zi extends Fu
{
public void show()
{
System.out.println("zi show");
}
public void method()
{
System.out.println("zi method");
}
}
class Test
{
public static void main(String[] args)
{
Fu f = new Zi();
f.show();
}
}
3.:看程序写结果(先判断有没有问题,如果没有,写出结果)
class A
{
public void show()
{
show2();
}
public void show2()
{
System.out.println("我");
}
}
class B extends A
{
public void show2()
{
System.out.println("爱");
}
}
class C extends B
{
public void show()
{
super.show();
}
public void show2()
{
System.out.println("你");
}
}
public class Test
{
public static void main(String[] args)
{
A a = new B();
a.show();
B b = new C();
b.show();
}
}
多态的好处和弊端
多态的好处
提高了程序的维护性(由继承保证)
提高了程序的扩展性(由多态保证)多态的弊端
不能访问子类特有功能
那么我们如何才能访问子类的特有功能呢?
多态中的转型
多态中的转型问题
向上转型
从子到父
父类引用指向子类对象向下转型
从父到子
父类引用转为子类对象
抽象类概述
抽象类概述
我说动物,你知道我说的是什么动物吗?只有看到了具体的动物,你才知道,这是什么动物。 所以说,动物本身并不是一个具体的事物,而是一个抽象的事物。只有真正的猫,狗才是具体的动物。同理,我们也可以推想,不同的动物吃的东西应该是不一样的,所以,我们不应该在动物类中给出具体体现,而是应该给出一个声明即可。在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。
抽象类特点
- 抽象类和抽象方法必须用abstract关键字修饰
格式
abstract class 类名 {}
public abstract void eat();抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类不能实例化
那么,抽象类如何实例化呢?
按照多态的方式,由具体的子类实例化。其实这也是多态的一种,抽象类多态。抽象类的子类
要么是抽象类
要么重写抽象类中的所有抽象方法
抽象类的成员特点
成员变量
可以是变量
也可以是常量构造方法
有构造方法,但是不能实例化
那么,构造方法的作用是什么呢?
用于子类访问父类数据的初始化成员方法
可以有抽象方法 限定子类必须完成某些动作
也可以有非抽象方法 提高代码复用性abstract不能和哪些关键字共存
private 冲突
final 冲突
static 无意义
接口概述
我们想想狗一般就是看门,猫一般就是作为宠物了,对不。但是,现在有很多的驯养员或者是驯兽师,可以训练出:猫钻火圈,狗跳高,狗做计算等。而这些额外的动作,并不是所有猫或者狗一开始就具备的,这应该属于经过特殊的培训训练出来的,对不。所以,这些额外的动作定义到动物类中就不合适,也不适合直接定义到猫或者狗中,因为只有部分猫狗具备这些功能。所以,为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现,将来哪些猫狗需要被培训,只需要这部分猫狗把这些额外功能实现即可。
接口特点
接口用关键字interface表示
格式:interface 接口名 {}
类实现接口用implements表示
格式:class 类名 implements 接口名 {}
接口不能实例化
那么,接口如何实例化呢?
按照多态的方式,由具体的子类实例化。其实这也是多态的一种,接口多态。接口的子类
要么是抽象类
要么重写接口中的所有抽象方法
接口成员特点
成员变量
只能是常量
默认修饰符 public static final构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
成员方法
只能是抽象方法
默认修饰符 public abstract
类与类,类与接口以及接口与接口的关系
类与类
继承关系,只能单继承,但是可以多层继承
类与接口
实现关系,可以单实现,也可以多实现。还可以在继承一个类的同时实现多个接口
接口与接口
继承关系,可以单继承,也可以多继承
抽象类和接口的区别
成员区别
抽象类 变量,常量;有抽象方法;抽象方法,非抽象方法
接口 常量;抽象方法关系区别
类与类 继承,单继承
类与接口 实现,单实现,多实现
接口与接口 继承,单继承,多继承设计理念区别
抽象类 被继承体现的是:”is a”的关系。共性功能
接口 被实现体现的是:”like a”的关系。扩展功能
形式参数和返回值
形式参数
基本类型
引用类型返回值类型
基本类型
引用类型链式编程
1:形式参数的问题在前面匿名对象的时候已经讲解过了。
但是,今天又多了抽象类和接口类型作为形式参数。
形式参数
类:需要的是该类的对象
抽象类:需要的是该抽象类的子类对象
接口:需要的是接口的子类对象
具体类作为形式参数:
class Student {
public void show() {
System.out.println(“show”);
}
}
class StudentDemo {
//如果参数是一个类名,那么实际需要的是一个具体的对象
public void method(Student s) {
s.show();
}
}
class StudentTest {
public static void main(String[] args) {
//如何调用StudentDemo中的method方法呢?
StudentDemo sd = new StudentDemo();
Student s = new Student();
sd.method(s);
}
}
抽象类作为形式参数:
abstract class Person {
public abstract void show();
}
class PersonDemo {
public void method(Person p) {
p.show();
}
}
//自己定义类继承自Person类
class PersonTest {
public static void main(String[] args) {
//如何调用PersonDemo中的method方法呢?
PersonDemo pd = new PersonDemo ();
Person p = new Student();
pd.method(p);
}
}
接口作为形式参数,类似抽象类作为形式参数。
2:返回值的问题
基本类型 返回什么就用什么接收。
引用类型
类:其实返回的是该类的对象
抽象类:其实返回的是该类的子类对象
接口:其实返回的是该接口的子类对象
具体类作为返回值类型:
class Student {
public void show() {
System.out.println(“show”);
}
}
class StudentDemo {
public Student getStudent() {
//Student s = new Student();
//return s;
return new Student();
}
}
class StudentTest {
public static void main(String[] args) {
//如何测试呢?
//原本我可以直接通过Student创建对象,调用功能
//但是现在不让这样做,怎么办呢?
StudentDemo sd = new StudentDemo();
Student s = sd.getStudent();
s.show();
}
}
抽象类作为返回值类型:
abstract class Person {
public abstract void show();
}
class PersonDemo {
public Person getPerson() {
Person p = new ???();
return p;
return new ???();
}
}
//自己定义类继承自Person类,否则PersonDemo代码无法完成?
class PersonTest {
public static void main(String[] args) {
//如何调用PersonDemo中的method方法呢?
PersonDemo pd = new PersonDemo ();
Person p = pd.getPerson();
p.show(); //其实调用的是Student的
}
}
接口作为返回值类型,类似抽象类作为返回值类型
3:链式编程的案例演示
new PersonDemo().getPerson().show();
包
包的概述
其实就是文件夹
作用:对类进行分类管理
包的划分:
举例:
学生的增加,删除,修改,查询
老师的增加,删除,修改,查询
以及以后可能出现的其他的类的增加,删除,修改,查询
基本的划分:按照模块和功能分。
高级的划分:就业班做项目的时候你就能看到了。
包的定义及注意事项
定义包的格式
package 包名;
多级包用.分开即可注意事项:
package语句必须是程序的第一条可执行的代码
package语句在一个java文件中只能有一个
如果没有package,默认表示无包名
带包的类的编译和运行
手动式
a:javac编译当前类文件。
b:手动建立包对应的文件夹。
c:把a步骤的class文件放到b步骤的最终文件夹下。
d:通过java命令执行。注意了:需要带包名称的执行
java cn.itcast.HelloWorld自动式
a:javac编译的时候带上-d即可
javac -d . HelloWorld.java
b:通过java命令执行。和手动式一样
导包
导包概述
不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了导包的功能。
导包格式
import 包名;
注意:
这种方式导入是到类的名称。
虽然可以最后写*,但是不建议。
权限修饰符
类及其组成可以用的修饰符类:
- 类:
默认,public,final,abstract
我们自己定义:public居多 成员变量:
四种权限修饰符均可,final,static
我们自己定义:private居多构造方法:
四种权限修饰符均可,其他不可
我们自己定义:public 居多成员方法:
四种权限修饰符均可,fianl,static,abstract
我们自己定义:public居多
内部类概述
把类定义在其他类的内部,这个类就被称为内部类。
举例:在类A中定义了一个类B,类B就是内部类。
内部类的访问特点:
内部类可以直接访问外部类的成员,包括私有。
外部类要访问内部类的成员,必须创建对象
内部类位置
按照内部类在类中定义的位置不同,可以分为如下两种格式:
成员位置(成员内部类)
局部位置(局部内部类)成员内部类
外界如何创建对象
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
成员内部类
一般内部类就是不让外界直接访问的。
举例讲解这个问题:Body和Heart,电脑和CPU。
成员内部的常见修饰符
private 为了保证数据的安全性
static 为了让数据访问更方便
1.被静态修饰的成员内部类只能访问外部类的静态成员
2.内部类被静态修饰后的方法
a.静态方法
b.非静态方法
注意:
1:非静态的成员内部类,成员只能是非静态的。
2:内部类被静态修饰后的方法有静态和非静态之分。他们的访问和不用静态是不一样的。
访问非静态方法:外部类名.内部类名 对象名 = new 外部类名.内部类名();
访问静态方法:上面创建的对象访问,或者外部类名.内部类名.方法名();
成员内部类面试题
补齐程序(注意:内部类和外部类没有继承关系)
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println(?);
System.out.println(??);
System.out.println(???);
}
}
}
在控制分别输出:30,20,10
答案:
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println(num);
System.out.println(this.num);
System.out.println(Outer.this.num);
}
}
}
class OuterDemo {
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
局部内部类
- 可以直接访问外部类的成员
- 可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能
局部内部类访问局部变量的注意事项:
必须被final修饰?
为什么呢?
因为局部变量会随着方法的调用完毕而消失,这个时候,局部对象并没有立马从堆内存中消失,还要使用那个变量。为了让数据还能继续被使用,就用fianl修饰,这样,在堆内存里面存储的其实是一个常量值。通过反编译工具可以看一下。
class Outer {
public void method() {
final int n = 100;
class Inner {
public void show() {
System.out.println(n);
}
}
Inner i = new Inner();
i.show();
}
}
class OuterDemo {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
匿名内部类
- 就是内部类的简化写法。
前提:存在一个类或者接口
这里的类可以是具体类也可以是抽象类。
格式:
new 类名或者接口名() {重写方法;}
本质:
是一个继承了类或者实现了接口的子类匿名对象
匿名内部类在开发中的使用
首先回顾我们曾经讲过的方法的形式参数是引用类型的情况,重点是接口的情况,我们知道这里需要一个子类对象。而匿名内部类就是一个子类匿名对象,所以,可以使用匿名内部类改进以前的做法。
abstract class Person {
public abstract void show();
}
class PersonDemo {
public void method(Person p) {
s.show();
}
}
class PersonTest {
public static void main(String[] args) {
//如何调用PersonDemo中的method方法呢?
PersonDemo pd = new PersonDemo ();
pd.method(new Person() {
public void show() {
System.out.println(“show”);
}
});
}
}