面向对象通识19(内部类)

定义在类体中定义的类(包含内部类的类被称为外部类)

基本语法和外部类相同,区别:

  • 可以使用以下三种修饰符
    • 内部类可以用static修饰,表明该内部类为外部类的一个实例
    • 可以用private修饰,表明该内部类只能在外部类中被访问
    • 可以用protected修饰,表名该内部类能在其外部类中被访问,也可以被其子类访问
  • 非静态内部类不能拥有静态成员
  • 内部类可以直接访问外部类的私有成员,但static内部类不能直接访问外部类的非static成员

意义

  • 当某个类及其实例必须要依附于别的类的存在而存在,就需要定义该类的内部类(内部接口、内部枚举)
  • 内部类可以提供更好的封装,可以通过private修饰让它完全隐藏于外部类

特性

内部类可以直接访问外部private的成员变量

  1. public class Cow {
  2. private double weight;
  3. public class CowLeg{
  4. public void test(){
  5. System.out.println("test方法");
  6. //直接访问外部类的private成员变量
  7. System.out.println("weight成员变量"+weight);
  8. }
  9. }
  10. }

内部类区分变量

举例:

  1. public class Dog {
  2. private int len=20;
  3. private class DogTail{
  4. private int len=200;
  5. public void info(){
  6. System.out.println("len:"+len);
  7. }
  8. }
  9. static DogTail dt;
  10. public static void main(String[] args) {
  11. dt=new DogTail();//编译错误,static方法访问非static内部类
  12. }
  13. }

内部类可以直接访问外部类的成员变量,但如果内部类的方法、内部类的成员变量与外部类的成员变量重名时,就需要区分。

  1. class Dog1{
  2. protected int len=2;
  3. }
  4. public class Dog extends Dog1{
  5. private int len=20;
  6. private class DogTail{
  7. private int len=200;
  8. public void info(){
  9. int len=2000;
  10. System.out.println("len:"+len);
  11. //就近访问局部变量
  12. System.out.println("len:"+this.len);
  13. //this.引用,限定访问当前类的实例变量
  14. System.out.println("len:"+Dog.this.len);
  15. //外部类.this.引用,限定访问当前类的外部类的实例变量
  16. System.out.println("len:"+Dog.super.len);
  17. //外部类.super.引用,限定访问当前类的外部类的父类的实例变量
  18. }
  19. }
  20. static DogTail dt;
  21. public void test(){
  22. //实例方法调用非静态内部类
  23. dt=new DogTail();
  24. dt.info();
  25. }
  26. public static void main(String[] args) {
  27. Dog d =new Dog();
  28. d.test();
  29. }
  30. }
  31. /*
  32. len:2000
  33. len:200
  34. len:20
  35. len:2
  36. */

在外部类以外使用静态内部类

  • 基本上使用内部类与试用版其他类没什么区别,唯一注意,static成员不能使用非static内部类创建实例

(如果只是声明变量,不算主动使用)

  • 在外部类以外使用静态内部类
    该内部类一定不能用private修饰;
    • 声明变量:外部类.静态内部类 变量名
    • 创建对象:new 外部类.静态内部类构造器
    • 调用方法:外部类.静态内部类.
    • 派生子类:extands 外部类.静态内部类(注意构造器调用)
      综上,把外部类名当做静态内部类的包名调用即可

举例:

  1. public class StaticOuter {
  2. //定义内部类
  3. public static class Inner {
  4. private int age;
  5. public Inner(int age) {
  6. this.age = age;
  7. }
  8. public void inTest() {
  9. System.out.println("静态内部类的测试方法");
  10. }
  11. public static int sum(int a, int b) {
  12. return a + b;
  13. }
  14. @Override
  15. public String toString() {
  16. return "Inner[age=" + this.age + "]";
  17. }
  18. }
  19. }
  1. public class InnerTest {
  2. public static void main(String[] args) {
  3. StaticOuter.Inner nm=new StaticOuter.Inner(4);//声明变量、创建对象
  4. StaticOuter.Inner.sum(2,3);//调用方法
  5. nm.inTest();
  6. }
  7. }
  8. class Outer extends StaticOuter.Inner{
  9. //派生子类
  10. public Outer() {
  11. super(3);
  12. }
  13. }

在外部类使用非静态的内部类

  • 内部类:也叫寄生类
    • static内部类的实例,要寄生在外部类本身之中
      • 只要用到该内部类,外部类就会被自动初始化
      • static内部类的实例,要寄生在外部类的实例之中
  • 外部类:也叫宿主类
  • 在外部类使用非静态的内部类:
    • 声明变量:外部类.静态内部类 变量名
    • 创建对象:宿主.new 费静态内部类构造器
      • 所谓宿主就是用外部类创建的对象名称
        或者:new 外部类构造器.new 内部类构造器
        第一个new的目的仅仅是为了得到宿主对象
    • 访问类变量(只能是常量):外部类.非静态内部类.类常量
    • 派生子类:extands 外部类.非静态内部类
      重点在子类构造器第一行,必须用到的语法:
      宿主.super(参数)

非静态内部类(包括其子类)的实例的素质是外部类的实例,因此必须由开发者创建外部类实例作为宿主

举例:

  1. public class inClass {
  2. class B {
  3. public void test() {
  4. System.out.println("仅仅是一个非静态的内部类");
  5. }
  6. }
  1. public class InnerTest {
  2. inClass.B in;
  3. inClass A=new inClass();
  4. //创建对象:宿主.new非静态内部类的构造器(参数);此处的A为宿主对象
  5. in=A.new B();
  6. //第一个new是主动创建一个宿主对象
  7. inClass.B iin=new inClass().new B();
  8. }
  9. class Outer1 extends inClass.B{
  10. public Outer1(){
  11. new inClass().super();
  12. }
  13. }

局部内部类

局部内部类放在方法中定义

局部内部类也只能用final修饰

(just like 局部变量)

  1. public class LocalInner {
  2. public void a(){
  3. //局部内部类
  4. class In{
  5. }
  6. }
  7. public void b(){
  8. //局部内部类
  9. class In{
  10. }
  11. }
  12. }

局部内部类只能在所定义的方法中使用,局限性很强,所以用的很少

局部内部类的类文件名为外部类$N内部类.class

匿名内部类

(很常用,普遍存在于安卓开发)

匿名内部类是没有名字的类,只能在创建的时候立即创建实例,以后不能复用该类

语法:

  1. new 父类构造器(参数)|接口(){
  2. //类体部分
  3. //一般的,类体部分就是实现抽象方法,因为没有类名,所以可以创建四大成员但是无法调用
  4. }

从上述语法可以看出:

  • 匿名内部类必须要显示集成一个父类或者实现一个接口,不能同时实现多个接口。
  • 不能实现父类的同时实现接口
  • 匿名内部类不能是抽象类,因此必须实现抽象接口或是抽象父类中的所有抽象方法
  • 可以定义出了构造器以外的四大成员,因为构造器的名字要和类名相同

举例:

  1. public class 匿名内部类 {
  2. public static void main(String[] args) {
  3. animal an=new animal(2.3)
  4. //向上转型:此处用animal的子类的实例赋值
  5. {
  6. @Override
  7. public void taste(){
  8. System.out.println("bad");
  9. }
  10. public void food(){
  11. System.out.println("为匿名内部类创建方法");
  12. }
  13. };//匿名内部类的类体
  14. an.taste();
  15. an.walk();
  16. //an编译时的类型是animal,不存在food方法
  17. Printable p=new Printable() {
  18. //类体部分只要实现抽象方法即可
  19. @Override
  20. public void print() {
  21. System.out.println("打印数据");
  22. }
  23. @Override
  24. public void inputDta() {
  25. System.out.println("输入数据");
  26. }
  27. };
  28. p.print();
  29. p.inputDta();
  30. }
  31. }
  32. abstract class animal{
  33. private double weight;
  34. animal(double weight){
  35. this.weight=weight;
  36. }
  37. public void walk(){
  38. System.out.println("Just walk.");
  39. }
  40. public abstract void taste();
  41. }
  42. interface Printable{
  43. //默认修饰符public和abstract
  44. void print();
  45. void inputDta();
  46. }