学习内容:
一、四种权限修饰符
在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限。四种访问权限修饰符如下:
- public
- protected
- (default)
- private
下面给出四种权限修饰符的访问能力:
由表中可以看出,权限由大到小排列下来顺序是:
public > protected > (default) > private
一般地:
- 成员变量使用 private ,隐藏细节。
- 构造方法使用 public ,方便创建对象。
- 成员方法使用 public ,方便调用方法。
二、内部类
1.概述
如果一个事物内部包含另一个事物,那么这就是一个类内部包含另一个类。
例如:身体和心脏的关系。又如:汽车和发动机的关系。
内部类有两种:
- 成员内部类
- 局部内部类(包含匿名内部类)
2.成员内部类(定义在类中方法之外的类)
如果一个类是定义在一个类的方法之外,那么这个类叫做成员内部类。
格式:
修饰符 class 类名称 {
修饰符 class 类名称 {
//...
}
//...
}
成员内部类适用于:一个事物内部还包含其他事物的情况。
访问特点:
内部类可以直接访问外部类的成员,包括私有成员。
外部类要访问内部类的成员,必须要建立内部类的对象。
访问方式:
- 间接方式:在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。
- 直接方式,公式:
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
下面进行简单的代码演示:
先定义一个类Body,在其中再定义一个成员内部类Heart
public class Body { // 外部类
public class Heart { // 成员内部类
// 内部类的方法
public void beat() {
System.out.println("心脏跳动:蹦蹦蹦");
System.out.println("我叫" + name);
}
}
private String name;
// 外部类的方法
public void methodBody() {
System.out.println("外部类的方法");
new Heart().beat();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
然后定义一个测试类,来访问成员内部类Heart:
public class Demo01InnerClass {
public static void main(String[] args) {
// 间接调用
Body body = new Body(); // 外部类的对象
// 通过外部类的对象,调用外部类的方法,里面间接再使用内部类Heart
body.methodBody();
System.out.println("------------------------");
// 直接调用,按照公式写:
Body.Heart heart = new Body().new Heart();
heart.beat();
}
}
输出结果如图所示:
3.内部类的同名变量访问
如果外部类的成员变量和内部类的成员变量和内部类的成员方法中的局部变量重名,这时候该如何去访问呢。我们通过一段代码来演示:
public class Outer {
int num = 10; // 外部类的成员变量
public class Inner /*extends Object*/ {
int num = 20; // 内部类的成员变量
public void methodInner() {
int num = 30; // 内部类方法的局部变量
System.out.println(num); // 局部变量,就近原则
System.out.println(this.num); // 内部类的成员变量
System.out.println(Outer.this.num); // 外部类的成员变量
}
}
}
总结:
访问本方法中的局部变量:直接写变量名称
访问本内部类中成员变量:this.变量名称
访问外部类中的成员变量:外部类名称.this.变量名称
我们定义一个测试类来进行验证:
public class Demo02InnerCalss {
public static void main(String[] args) {
// 外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
Outer.Inner obj = new Outer().new Inner();
obj.methodInner();
}
}
输出结果如图所示:
4.局部内部类(定义在类中方法之内的类)
如果一个类是定义在一个类的方法的内部的,那么这就是一个局部内部类。
格式:
修饰符 class 外部类名称 {
修饰符 返回值类型 外部类方法名称(参数列表) {
class 局部内部类名称 {
//...
}
}
}
使用特点:
“局部”:只有当前所属的方法才能使用它,出了这个方法外就不能使用。
使用方法:
通过创建对象来使用。
下面通过代码简单演示:
创建一个外部类,在外部类的方法中创建一个局部内部类:
public class Outer { // 外部类
public void methodOuter() {
class Inner { // 局部内部类
int num = 10;
public void methodInner() {
System.out.println(num); // 10
}
}
Inner inner = new Inner(); // 创建局部内部类的对象
inner.methodInner(); // 调用局部内部类的方法
}
}
然后定义测试类,通过创建对象来使用局部内部类:
public class DemoMain {
public static void main(String[] args) {
Outer obj = new Outer();
obj.methodOuter();
}
}
运行结果如图所示:
5.局部内部类的final问题
局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。下面用一段代码来做演示:
public class MyOuter {
public void methodOuter() {
final int num = 10; // 所在方法的局部变量,必须是有效final的
// num = 20; // 错误写法
class MyInner {
public void methodInner() {
System.out.println(num);
}
}
}
}
备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略。
原因:
- new出来的对象在堆内存当中。
- 局部变量是跟着方法走的,在栈内存当中。
- 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
- 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
也就是说,对象的生存周期会比局部变量的要长,如果在方法运行结束之后,局部变量又发生了变化,此时对象是不知道的,这种情况是不允许的。
6.重点!!!匿名内部类
匿名内部类 :是内部类的简化写法。它的本质是一个 带具体实现的 父类或者父接口的 匿名的 子类对象。开发中,最常用到的内部类就是匿名内部类了。
当我们使用一个接口时,需要定义实现类、重写接口中的方法、创建实现类对象、调用重写后的方法。
如此繁琐的步骤,我们使用匿名内部类,可以把它合成一步来做。
如果接口的实现类或者是父类的子类,只需要使用唯一的一次,那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。
格式:
接口名称 对象名 = new 接口名称() {
// 覆盖重写接口中所有抽象方法
};
对格式“new 接口名称() {…};”进行解析:
- new代表创建对象的动作
- 接口名称就是匿名内部类需要实现哪个接口
- {…}这才是匿名内部类的内容
另外还要注意几点问题:
- 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
如果希望多次创建对象,而且类的内容一样的话,那么就必须使用单独定义的实现类了。 - 匿名对象在【调用方法】的时候,只能调用唯一一次。
如果希望同一个对象调用多次方法,那么必须给对象取个名字。 - 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】
下面用代码简单演示:
首先创建一个接口,接口中定义两个抽象方法:
public interface MyInterface {
public abstract void method1(); // 抽象方法
public abstract void method2(); // 抽象方法
}
然后定义一个测试类,来体现上面我们所说的注意事项:
public class DemoMain {
public static void main(String[] args) {
// MyInterface impl = new MyInterfaceImpl();
// impl.method();
// 使用匿名内部类,是匿名内部类,但不是匿名匿名对象,对象名称就叫objA
MyInterface objA = new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法111A");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法222A");
}
}; // 注意这里的分号
objA.method1();
objA.method2();
System.out.println("-------------------------");
// 使用了匿名内部类,而且省略了对象名称,既是匿名内部类也是匿名对象
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法111B");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法222B");
}
}.method1();
//因为匿名对象无法调用第二次方法,所以需要再创建一个匿名内部类的匿名对象
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名内部类实现了方法111B");
}
@Override
public void method2() {
System.out.println("匿名内部类实现了方法222B");
}
}.method2();
}
}