我们知道java是一门面向对象的语言,面向对象的语言有三大特性:封装,继承和多态。
在类和对象的阶段,主要研究的是封装特性。
1.封装
1.1封装的概念
封装性简单来说就是将具体功能是如何实现的细节给封装起来,不让外界知道,但会提供接口来实现这个功能。
以手机为例,我们平常看到的只是手机的外壳,这些外壳把手机的内部细节给封装起来了。但是我们可以通过屏幕这个接口来实现手机的各种功能,而且我们并不知道手机内部是如何实现这种功能的具体细节。这就构成了封装性。
封装:将数据和操作数据的方法进行有机结合,隐藏类的内部细节和实现方法,仅提供接口与外界进行交互。
class Student{
private String name;
private int age;
private String sex;
}
如以上代码,我们主要通过private来对类的成员变量和成员方法进行封装。
1.2 访问控制权限
那封装性在Java中是如何体现的呢?
Java中主要通过类和访问控制权限来实现封装性,类可以将数据和操作封装数据的方法结合起来,访问控制权限决定成员变量或者成员方法能否直接在外界使用。在Java中主要提供了4中访问权限,分别为public、private、protected和default,这些具体被称为访问修饰限定符。具体细节如下图所示
说明事项:
1.protected主要在继承中用到,后面继承和多态的部分会介绍。
2.当成员变量或者成员方法前面没写访问修饰限定符,会默认为default。
3.访问修饰限定符不仅可以控制成员变量和成员方法的访问权限,也可以控制类的访问权限。
class Student{
public int score;//public属性
protected String name;//protected属性
int age;//default属性
private String sex;//private属性
}
1.3 private属性
前面我们提到了类的封装主要通过private访问修饰限定符来实现。
当我们在一个类中用了private修饰成员变量或者成员方法,外部类是不能直接访问用private修饰的成员变量或者成员方法。
class Student{
private int age;
private String name;
private String sex;
}
public class Demo {
public static void main(String[] args) {
Student stu1=new Student();
stu1.age=10;
stu1.name="zhangsan";
stu1.sex="male";
}
}
运行以上代码会报以下的错误
原因就是在Student类中的age、name和sex都用了private修饰,外部类是不能直接使用这三个成员变量的。
解决方案:我们可以通过在本部类中的getXxx()和setXxx()方法来间接访问这三个成员变量。
class Student{
private int age;
private String name;
private String sex;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public void Print(){
System.out.println("age:"+age+" "+"name:"+name+"sex:"+sex);
}
}
class Demo{
public static void main(String[] args) {
Student stu1=new Student();
stu1.setAge(12);
System.out.println(stu1.getAge());
stu1.setName("zhangsan");
System.out.println(stu1.getName());
stu1.setSex("male");
System.out.println(stu1.getSex());
stu1.Print();
}
}
如以上代码,我们通过getXxx()和setXxx()方法间接访问了Student类中用private修饰的三个成员变量。通过SetXxx()来初始化,通过getXxx()来获取初始化的值。
以下是快速构建getXxx()和setXxx()方法的快捷键。
在对应的类中按鼠标右键,点击Generate,在点击GetterandSetter,接着按住CTRL键来选择成员变量。
1.4 对我之前一个疑惑的解决
一开始我感觉很奇怪,我们明明都封装好了,为什么外部还能正确的访问我们内部封装好的属性。
我的理解:因为类内部为我们提供了很多使用本类中用private修饰的成员变量和成员方法的接口,也就是方法,这些接口根据我们用户外部的输入情况来调用类中合适的接口来实现对应的操作,具体是内部如何实现这些操作的,我们用户并不知道,所以这与封装性并不矛盾。
2.封装扩展之包
对于包是什么,相信大家都很疑惑,在同一个包中的类,同一个包中的不同类,不同包中的不同类,都是什么意思呢?
2.1包的理解
在面向对象的体系中,有一个软件包的概念,即为了更好得管理类,我们将多个类放在同一个包中,即软件包。
包可以理解成文件夹。
我们以管理音乐为例,假设我们要将相同类型的音乐放在同一个文件夹里面或者将同一个作者的音乐放在同一个文件夹里面。
在Java中也导入了包的概念,包是对类和接口封装性的体现,是一种对类和接口很好的组织方式。如:在一个包中的类不能被其他的包中的类使用。但是不同包中的类也可能存在重名的情况。
2.2 如何导入包中的类
import java.util.Date; //导入包
public class Test {
public static void main(String[] args) {
Date date=new Date();
System.out.println(date.getDate());
}
}
如上面代码所示,import java.util.Date 就是一个导入了java.util包中的Date类,我们根据Date类创建了一个date对象,date可以直接调用Date类中已经包装好了的方法,如上面的date.getDate()是获取该月的第几号。
如过我们还要使用java.util包中其他的类,我们可以写成 import java.util.* 的形式,这样我们就可以调用该包中的其他类。如以下代码。
import java.util.*;
public class Test {
public static void main(String[] args) {
Date date=new Date();
System.out.println(date.getDate());
System.out.println(date.getTime());
}
}
但是我们不建议写成 import java.util.* 这样的形式,因为不同包中有重名的类,如果不处理好就会报错。
import java.util.*;
import java.sql.*;
public class Test {
public static void main(String[] args) {
Date date=new Date();
System.out.println(date.getDate());
System.out.println(date.getTime());
}
}
如以上代码就是会报错的,因为导入的两个的包中都有Date为名字的类,给编译器造成了混淆,编译出错。
这时候我们就要使用完整的类名。
import java.util.*;
import java.sql.*;
public class Test {
public static void main(String[] args) {
java.util.Date date=new java.util.Date();//使用完整类名,明确哪个包
System.out.println(date.getDate());
System.out.println(date.getTime());
}
}
我们可以使用import static 导入包中的静态方法和和成员变量。
看一下代码
import java.lang.Math.*;
public class Test2 {
public static void main(String[] args) {
double x=30;
double y=40;
double ret=Math.pow(x,2)+Math.pow(y,2);
}
}
当我们换一种导入包的写法,包中对应的静态方法就不用写类名了。
import static java.lang.Math.*;
public class Test2 {
public static void main(String[] args) {
double x=30;
double y=40;
double ret=pow(x,2)+pow(y,2);
}
}
2.3 自定义的包
这下我们来解决前面遗留的问题,什么是不同包中的类,同一个包中的不同类。
这里我们先来学会建立自定义的包。
右击src,点击New,再点击Package,就会跳出以下界面。
接着在输入框中输入你的包名就行了。
当我们建立自己的包之后,如上图所示,就是我建立的一个名字为Demo的包,接着我们在这个包中在建立类就行了。
在自己的包中建立类之后,会自动跳出以下代码。
package Demo;
public class Test1 {
}
第一行代码表名我们创建的Test1类是在哪一个包中。
接下来我们来介绍同一个包中的不同类和同一个包中的同一类。
package Demo;
public class Test1 {
int a;
public void test(){
System.out.println(a);
}
}
如以上代码,a是default属性,可以在同包同类中访问,也可以同包不同类访问。以上代码就是同包同类。
上图就是同一个包中不同类,在另一个类,照样可以访问到a。
但如果我们将a改变为private属性,同一个包中的不同类就不能访问了。
如下图,就会报错。