2. 封装
2.1 什么是Java中的封装
将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。
使用封装的好处有哪些?
- 只能通过规定的方法访问数据
- 隐藏类的实例细节,方便修改和实现。
封装的实现步骤是什么?
- 第一步:修改属性的可见性,即:设为private修饰符
- 第二步:创建get/set方法,用于属性的读写
- 第三步:在get/set方法中加入属性控制语句,用于对属性值的合法性判断
我们发现之前在写Telephone.java这个类的属性的时候,属性前面并没有加任何的修饰符,所以在其他文件(比如main()方法中)是可以直接访问和操作属性值的,从而来修改属性的原始值。我们说,这种方式其实是不好的,应该用封装的方式将属性信息隐藏起来,给用户一个特定方法让他去访问我们的属性值。此时,我们需要进行以下操作:
首先,在Telephone.java类中定义成员变量,并加上private修饰符,接着定义属性的get和set方法,如下:
package com.example;
public class Telephone {
// 1. 定义类的成员变量,并加上private修饰符
private float screen;
private float cpu;
private float mem;
// 2. 定义成员变量的get和set方法
public float getScreen(){
return screen;
}
public void setScreen(float newScreen){
screen = newScreen;
}
// 自定义的无参构造方法
public Telephone(){
System.out.println("执行了无参构造方法!");
}
// 有参的构造方法,目的是给成员变量复制
public Telephone(float newScreen, float newCpu, float newMem){
if(newScreen < 3.5f) {
System.out.println("您输入屏幕尺寸参数有问题,自动为你赋值3.5");
screen = 3.5f;
}else{
screen = newScreen;
}
cpu = newCpu;
mem = newMem;
System.out.println("执行了有参构造方法!");
}
}
然后,main()方法中修改如下:
package com.example;
public class Main {
public static void main(String[] args) {
// write your code here
// 通过无参的构造方法可以创建对象
// Telephone phone = new Telephone();
// 通过有参的构造方法也可以创建对象,并给对象中的实例变量赋初值
Telephone phone2 = new Telephone(1.5f, 4.0f, 64.0f);
// 3. 给属性赋值,然后得到属性值
phone2.setScreen(6.7f);
System.out.println("phone2的screen:" + phone2.getScreen());
}
}
我们来看下执行结果,如下:
您输入屏幕尺寸参数有问题,自动为你赋值3.5
执行了有参构造方法!
phone2的screen:6.7
2.2 使用包管理Java中的类
我们在封装的时候,经常会遇到一个问题:封装的信息中,类的名字很可能会出现冲突,那怎么来解决这个问题呢?这个时候Java中的包就派上用场了。
1、包的作用
- 管理Java文件
- 解决同名文件冲突
2、包的定义:package 包名
- 必须放在Java源程序的第一行
- 包名间可以使用“.”号隔开. 如:com.nwd.example.
3、系统中的包
格式:
如:
- java.lang.(类) //包含Java语言基础的类
- java.util.(类) //包含Java语言中各种工具类
- java.io.(类) //包含输入、输出相关功能的类
4、包的使用
(1)可以通过import关键字,在某个文件使用其他文件中的类
如: import com.nwd.music.example
(2)Java中,包的命名规范是全小写字母拼写
(3)使用的时候不但可以加载某个包下的文件,也可以加载某个具体包下的文件
如:com.nwd.*
如:com.nwd.music.*
2.3 Java中的this关键字
1、this关键字代表当前对象
- this.属性:表示操作当前对象的属性
- this.方法:表示调用当前对象的方法
2、封装对象的属性的时候,经常会用到this关键字
get/set方法中,如下:
2.4 Java中的内部类
问题:什么是内部类呢?
答:内部类( Inner Class )就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类。
问题:那为什么要将一个类定义在另一个类里面呢?独立的一个类不好吗?
答:内部类的主要作用如下:
- 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类
- 内部类的方法可以直接访问外部类的所有数据,包括私有的数据
- 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便
下面我们通过一个简单的例子来了解一下内部类,首先创建一个类HelloWorld.java,然后在这个类的内部也创建一个叫做Inner的内,如下:
package com.example;
public class HelloWorld {
private String name = "啪啪啪";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//在HelloWorld类中定义一个内部类 Inner
public class Inner{
//定义 内部类的方法
public void show() {
System.out.println("执行了HelloWorld类的内部类Inner!");
System.out.println("外部类的 姓名 默认值是:" + name);
setName("你好,Hello");
System.out.println("内部类调用外部方法后,外部类的 姓名 属性值是:" + getName());
}
}
}
然后,在main方法中实例化这两个类的对象,并调用Inner类对象的方法,如下:
package com.example;
public class Main {
public static void main(String[] args) {
// write your code here
//1.创建外部类的对象
HelloWorld hello = new HelloWorld();
//2.创建内部类的对象
HelloWorld.Inner inn = hello.new Inner();
//3.调用内部类的方法
inn.show();
}
}
接着,我们来执行一下main方法,结果如下:
执行了HelloWorld类的内部类Inner!
外部类的 姓名 默认值是:啪啪啪
内部类调用外部方法后,外部类的 姓名 属性值是:你好,Hello
2.5 Java中的成员内部类
内部类中最常见的就是成员内部类,也称为普通内部类。我们来看如下代码:
首先创建了一个内Oute.java,其中有一个私有变量a,默认初值为99,并在这个Outer类中还创建了一个Inner类,如下:
package com.example;
public class Outer {
private int a = 99; //外部类的私有属性
public class Inner {
int b = 2;
public void test() {
System.out.println("访问外部类中的a:" + a);
System.out.println("访问内部类中的b:" + b);
}
}
}
接着,在main方法中,先实例化一个外部类Outer的对象,然后用这个外部类对象去创建一个内部类Inner的对象,最后用这个内部类对象去调用内部类的方法,如下:
package com.example;
public class Main {
public static void main(String[] args) {
// write your code here
Outer o = new Outer(); //创建一个外部类对象 o
Outer.Inner i = o.new Inner(); //使用外部类对象创建一个内部类对象 i
i.test(); //内部类对象调用外部类对象的方法
}
}
接着,我们来执行一下main方法,结果如下:
访问外部类中的a:99
访问内部类中的b:2
从上面的代码中我们可以看到,成员内部类的使用方法:
1、 Inner 类定义在 Outer 类的内部,相当于 Outer 类的一个成员变量的位置,Inner 类可以使用任意访问控制符,如 public 、 protected 、 private 等;
2、 Inner 类中定义的 test() 方法可以直接访问 Outer 类中的数据,而不受访问控制符的影响,如直接访问 Outer 类中的私有属性a;
3、 定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,即:内部类 对象名 = 外部类对象.new 内部类( );
4、 编译上面的程序后,会发现产生了两个 .class 文件(存在于包中),如下:
其中,第二个是外部类的 .class 文件,第一个是内部类的 .class 文件,即成员内部类的 .class 文件总是这样:外部类名$内部类名.class
-
如果内部类和外部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法。而如果要访问外部类的成员变量或方法,可以使用“this”关键字。比如:
main方法不变,还是如下:
运行结果如下:
访问外部类中的a:99
访问内部类中的a:100
从结果看,使用了this关键字后,的确能够访问外部类的成员变量。
-
外部类不能直接使用内部类的成员或方法
2.6 Java中的静态内部类
静态内部类是 static 修饰的内部类,这种内部类的特点是:
1、 静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问
2、 如果外部类的静态成员与内部类的成员名称相同,可通过“外部类名.静态成员”访问外部类的静态成员;如果外部类的静态成员与内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员
3、 创建静态内部类的对象时,不需要外部类的对象,可以直接创建,即: 内部类 对象名= new 内部类()
基于上面3点,我们来实践一下。首先创建一个SOuter.java的外部类,同时在其内部创建一个内部类SInner,如下:
package com.example;
public class SOuter {
private int a = 99; // 外部类的私有成员变量
static int b = 10; // 外部类的静态成员变量
// 定义一个静态内部类
public static class SInner{
int b = 20; // (静态)内部类的成员变量
public void test() {
System.out.println("访问外部类中的b:" + SOuter.b);
System.out.println("访问内部类中的b:" + b);
}
}
}
main方法如下:
package com.example;
public class Main {
public static void main(String[] args) {
// write your code here
SOuter.SInner si = new SOuter.SInner(); // 直接创建静态内部类的对象
si.test(); // 调用test方法
}
}
程序运行结果如下:
访问外部类中的b:10
访问内部类中的b:20
2.7 Java中的方法内部类
方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。我们也来实践一下。
首先创建一个MOuter.java的外部类,这个外部类中有一个show()方法,然后我们在这个show方法中创建一个内部类MInner,如下:
package com.example;
public class MOuter {
// 外部类中的方法
public void show() {
final int a = 25; //定义一个外部类的常量
int b = 30;
//定义一个方法中的内部类
class MInner{
int c = 10; // 内部类中的变量
//内部类中的方法
public void print() {
System.out.println("访问外部类方法中的常量a:" + a);
System.out.println("访问内部类中的变量c:" + c);
}
}
MInner mi = new MInner(); // 创建方法内部类的对象
mi.print(); // 调用内部类的方法
}
}
main方法如下:
package com.example;
public class Main {
public static void main(String[] args) {
// write your code here
MOuter mo = new MOuter(); //创建外部类的对象
mo.show(); // 调用外部类方法
}
}
我们来看看运行结果,如下:
访问外部类方法中的常量a:25
访问内部类中的变量c:10
由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和 static 修饰符。