Java基础知识(11)- Java 面向对象(三)| 内部类 (Inner)、封装 (Encapsulation)
1. 内部类 (Inner)
在类内部可定义属性和方法,且在类内部也可以定义另一个类。如果在类 Outer 的内部再定义一个类 Inner,此时类 Inner 就称为内部类(或称为嵌套类),而类 Outer 则称为外部类(或称为宿主类)。
内部类是一个独立的类,在编译之后内部类会被编译成独立的 .class 文件,但是前面冠以外部类的类名和 $ 符号。
内部类分类:成员内部类(实例内部类)、静态内部类、局部内部类、匿名内部类
1) 成员内部类(或实例内部类)
(1) 在成员内部类中,不能定义 static 成员,除了静态常量(final static 修饰的变量);
(2) 在成员内部类中,可以访问外部类的所有成员;
(3) 在外部类中,不能直接访问内部类的成员变量和方法,必须通过内部类的实例来进行访问;
(4) 在外部类的静态方法中(或其他类中),必须通过外部类的实例创建内部类的实例来进行访问;
(5) 外部类实例与内部类实例是一对多的关系,也就是说一个内部类实例只对应一个外部类实例,而一个外部类实例则可以对应多个内部类实例;
实例:
1 class Outer { 2 3 int a = 1; 4 final String outerFinalMsg = "Outer final message"; 5 static String outerStaticMsg = "Outer static message"; 6 7 public String getFinalMessage() { 8 9 Inner inner = new Inner(); // 不需要创建外部类实例 10 return inner.innerStaticMsg; 11 } 12 13 public static String getStaticMessage() { 14 Inner inner = new Outer().new Inner(); // 需要创建外部类实例 15 return inner.innerStaticMsg; 16 } 17 18 // 成员内部类 19 class Inner { 20 private String name; 21 // 不能定义 static 成员变量,除了 final static 修饰 22 final static String innerStaticMsg = "Inner static message"; 23 24 public Inner() {} 25 26 public Inner(String name) { 27 this.name = name; 28 } 29 30 void display() { 31 32 // 直接访问外部类的成员 33 System.out.println("Inner(" + this.name + "): (a+1) = " + (a+1)); 34 System.out.println("Inner(" + this.name + "): outerFinalMsg = " + outerFinalMsg); 35 System.out.println("Inner(" + this.name + "): outerStaticMsg = " + outerStaticMsg); 36 System.out.println("Inner(" + this.name + "): getFinalMessage() = " + getFinalMessage()); 37 System.out.println("Inner(" + this.name + "): getStaticMessage() = " + getStaticMessage()); 38 } 39 40 // 不能定义 static 方法 41 //static void show() {} 42 } 43 44 Inner obj3 = new Inner("Object 3"); // 不需要创建外部类实例 45 46 } 47 48 public class App { 49 public static void main( String[] args ) { 50 51 // 方法1:先创建外部类对象,再创建内部类对象 52 Outer outer = new Outer(); 53 Outer.Inner obj1 = outer.new Inner("Object 1"); 54 obj1.display(); 55 56 // 方法2:同时创建外部类和内部类对象,只保存内部类对象引用 57 Outer.Inner obj2 = new Outer().new Inner("Object 2"); 58 obj2.display(); 59 60 // obj3 作为外部类的成员变量 61 outer.obj3.display(); 62 } 63 }
输出:
Inner(Object 1): (a+1) = 2
Inner(Object 1): outerFinalMsg = Outer final message
Inner(Object 1): outerStaticMsg = Outer static message
Inner(Object 1): getFinalMessage() = Inner static message
Inner(Object 1): getStaticMessage() = Inner static message
Inner(Object 2): (a+1) = 2
Inner(Object 2): outerFinalMsg = Outer final message
Inner(Object 2): outerStaticMsg = Outer static message
Inner(Object 2): getFinalMessage() = Inner static message
Inner(Object 2): getStaticMessage() = Inner static message
Inner(Object 3): (a+1) = 2
Inner(Object 3): outerFinalMsg = Outer final message
Inner(Object 3): outerStaticMsg = Outer static message
Inner(Object 3): getFinalMessage() = Inner static message
Inner(Object 3): getStaticMessage() = Inner static message
2) 静态内部类
静态内部类是指使用 static 修饰的内部类。
(1) 创建静态内部类的实例,不需要创建外部类的实例;
(2) 在静态内部类中,可以定义静态成员(静态变量和静态方法)和实例成员(成员变量和方法);
(3) 在静态内部类中,可以直接访问外部类的静态成员;
(4) 在静态内部类中,访问外部类的实例成员,需要通过外部类的实例去访问;
(5) 外部类以外的其他类,需要通过完整的类名访问静态内部类中的静态成员;
(6) 访问静态内部类中的实例成员,则需要通过静态内部类的实例;
实例:
1 class Outer2 { 2 3 int b = 2; 4 final String outer2FinalMsg = "Outer2 final message"; 5 static String outer2StaticMsg = "Outer2 static message"; 6 7 public String getFinalMessage() { 8 // 直接访问静态内部类 static 成员 9 return StaticInner.staticLabel; 10 } 11 12 public static String getStaticMessage() { 13 // 直接访问静态内部类 static 成员 14 return StaticInner.staticLabel; 15 } 16 17 // 静态内部类 18 static class StaticInner { 19 int x = 100; 20 static String staticLabel = "StaticInner Label"; 21 22 void display() { 23 24 // 访问外部类实例成员,需要通过外部类的实例去访问 25 Outer2 outer2 = new Outer2(); 26 System.out.println("StaticInner: (outer2.b + 1) = " + (outer2.b+1)); 27 System.out.println("StaticInner: outer2.outer2FinalMsg = " + outer2.outer2FinalMsg); 28 System.out.println("StaticInner: outer2.getFinalMessage() = " + outer2.getFinalMessage()); 29 30 // 直接访问外部类 static 成员 31 System.out.println("StaticInner: outer2StaticMsg = " + outer2StaticMsg); 32 System.out.println("StaticInner: getStaticMessage() = " + getStaticMessage()); 33 } 34 35 static void staticDisplay(String str) { 36 System.out.println("StaticInner: " + str); 37 } 38 } 39 } 40 41 public class App { 42 public static void main( String[] args ) { 43 // display() 是静态内部类的实例方法,所以需要 new 一个静态内部类实例对象 44 Outer2.StaticInner staticInnerObj = new Outer2.StaticInner(); 45 staticInnerObj.display(); 46 // 47 staticInnerObj.staticDisplay("staticInnerObj.x: " + staticInnerObj.x); 48 staticInnerObj.staticDisplay("staticInnerObj.label: " + staticInnerObj.staticLabel); 49 50 // 不需要创建外部类实例,可以直接访问静态内部类的静态成员 51 Outer2.StaticInner.staticDisplay("Outer2.StaticInner.staticLabel: " + Outer2.StaticInner.staticLabel); 52 } 53 }
输出:
StaticInner: (outer2.b + 1) = 3
StaticInner: outer2.outer2FinalMsg = Outer2 final message
StaticInner: outer2.getFinalMessage() = StaticInner Label
StaticInner: outer2StaticMsg = Outer2 static message
StaticInner: getStaticMessage() = StaticInner Label
StaticInner: staticInnerObj.x: 100
StaticInner: staticInnerObj.label: StaticInner Label
StaticInner: App.InnerStaticClass.staticLabel: StaticInner Label
3) 局部内部类
(1) 局部内部类与局部变量一样,不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰;
(2) 局部内部类只在当前方法中有效;
(3) 局部内部类中不能定义 static 变量和方法;
(4) 局部内部类中还可以包含内部类,但是这些内部类也不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰;
(5) 在局部内部类中可以访问外部类的所有成员;
(6) 在局部内部类中只可以访问当前方法中 final 类型的参数与变量。如果方法中的成员与外部类中的成员同名,则可以使用 <OuterClassName>.this.<MemberName> 的形式访问外部类中的成员;
实例:
1 class Outer3 { 2 3 int c = 3; 4 static int d = 4; 5 final String outer3FinalMsg = "Outer3 final message"; 6 static String outer3StaticMsg = "Outer3 static message"; 7 8 public String getFinalMessage() { 9 return outer3FinalMsg; 10 } 11 12 public static String getStaticMessage() { 13 return outer3StaticMsg; 14 } 15 16 public void testLocalInner() { 17 final int y = 777; 18 int z = 999; 19 20 // 局部内部类 21 class LocalInner { 22 23 // 不能定义 static 变量和方法 24 //static int i = 9; 25 //static void test() {} 26 27 // 可以直接访问外部类的所有成员 28 void display() { 29 // 访问外部类成员变量和方法 30 System.out.println("LocalInner: (c+1) = " + (c+1)); 31 System.out.println("LocalInner: getFinalMessage() = " + getFinalMessage()); 32 // 访问部类 static 变量和方法 33 System.out.println("LocalInner: (d+1) = " + (d+1)); 34 System.out.println("LocalInner: getStaticMessage() = " + getStaticMessage()); 35 36 // 只可以访问当前方法中 final 类型的参数与变量 37 System.out.println("LocalInner: y = " + y); 38 //System.out.println("LocalInner: z =" + z); 39 } 40 } 41 LocalInner localInner = new LocalInner(); 42 localInner.display(); 43 } 44 } 45 46 public class App { 47 public static void main( String[] args ) { 48 Outer3 outer3 = new Outer3(); 49 outer3.testLocalInner(); 50 } 51 }
输出:
LocalInner: (c+1) = 4
LocalInner: getFinalMessage() = Outer3 final message
LocalInner: (d+1) = 5
LocalInner: getStaticMessage() = Outer3 static message
LocalInner: y = 777
4) 匿名内部类
匿名类是指没有类名的内部类,必须在创建时使用 new 语句来声明类。
匿名类有两种实现方式:
(1) 继承一个类,重写其方法。
(2) 实现一个接口(可以是多个),实现其方法。
匿名类的特点:
(1) 匿名类和局部内部类一样,可以访问外部类的所有成员。如果匿名类位于一个方法中,则匿名类只能访问方法中 final 类型的局部变量和参数;
(2) 匿名类中允许使用非静态代码块进行成员初始化操作;
(3) 匿名类的非静态代码块会在父类的构造方法之后被执行;
实例:
1 class Outer4 { 2 void display() { 3 System.out.println("Outer4 -> display()"); 4 } 5 } 6 7 public class App { 8 9 static int f = 6; 10 static String staticMsg = "Anonymous static message"; 11 12 public static String getStaticMessage() { 13 return staticMsg; 14 } 15 16 public static void main( String[] args ) { 17 final int e = 5; 18 final String finalMsg = "Anonymous final message"; 19 Outer4 outer4 = new Outer4() { 20 void display() { 21 22 // 访问外部方法 final 变量 23 System.out.println("Anonymous(outer4): (e+1) = " + (e+1)); 24 System.out.println("Anonymous(outer4): finalMsg = " + finalMsg); 25 26 // 访问外部类 static 方法和变量 27 System.out.println("Anonymous(outer4): (f+1) = " + (f+1)); 28 System.out.println("Anonymous(outer4): getStaticMessage() = " + getStaticMessage()); 29 } 30 }; 31 outer4.display(); 32 33 } 34 }
输出:
Anonymous(outer4): (e+1) = 6
Anonymous(outer4): finalMsg = Anonymous final message
Anonymous(outer4): (f+1) = 7
Anonymous(outer4): getStaticMessage() = Anonymous static message
2. 封装 (Encapsulation)
封装是将类的某些信息隐藏在类内部,不允许外部程序直接访问,只能通过该类提供的方法来实现对隐藏信息的操作和访问。
要访问该类的代码和数据,必须通过严格的接口控制。封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
封装的优点:
1) 良好的封装能够减少耦合。
2) 类内部的结构可以自由修改。
3) 可以对成员变量进行更精确的控制。
4) 隐藏信息,实现细节。
实现封装的具体步骤如下:
1) 修改属性的可见性来限制对属性的访问,一般设为 private。
2) 为每个属性创建一对赋值(setter)方法和取值(getter)方法,一般设为 public,用于属性的读写。
3) 在赋值和取值方法中,加入属性控制语句(对属性值的合法性进行判断)。
实例:
1 class User { 2 private String name; // 姓名 3 private int age; // 年龄 4 private String phone; // 联系电话 5 private String address; // 家庭住址 6 7 public String getName() { 8 return name; 9 } 10 11 public void setName(String name) { 12 this.name = name; 13 } 14 15 public int getAge() { 16 return age; 17 } 18 19 public void setAge(int age) { 20 // 对年龄进行限制 21 if (age < 18 || age > 65) { 22 System.out.println("年龄必须在 18 到 65之间!"); 23 this.age = 35; // 默认年龄 24 } else { 25 this.age = age; 26 } 27 } 28 29 public String getPhone() { 30 return phone; 31 } 32 33 public void setPhone(String phone) { 34 this.phone = phone; 35 } 36 37 public String getAddress() { 38 return address; 39 } 40 41 public void setAddress(String address) { 42 this.address = address; 43 } 44 45 } 46 47 public class App { 48 public static void main( String[] args ) { 49 50 User user = new User(); 51 user.setName("Tester"); 52 user.setAge(32); 53 user.setPhone("1234567879"); 54 user.setAddress("Home address ..."); 55 System.out.println("Name: " + user.getName()); 56 System.out.println("Age: " + user.getAge()); 57 System.out.println("Phone: " + user.getPhone()); 58 System.out.println("Address: " + user.getAddress()); 59 60 } 61 }
输出:
Name: Tester
Age: 32
Phone: 1234567879
Address: Home address ...
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)