@月月鸟
封装概述
上面我们已经说到了Java是注重于类的编写,那么封装自然也是对类的封装。
对类封装有很多好处:
1 .可以彻底隐藏方法的内部实现,仅仅提供一个调用的方法给其他人,让其他使用这个类的人不需要关心是如何实现的,只要知道该如何调用就行。
2. 隐藏方法的内部实现的好处,可以让保留调用方法不变的同时,随意修改类的结构,而不影响其他人运行结果。
3 .封装还会分开类的属性,将类的属性分成私有属性和公共属性。私有属性仅供类自身调用,和公共属性也仅提供一个供外部调用的方法。
4. 按照软件的术语,良好的封装是能够减少耦合。
如何对一个类进行封装,则需要根据这个类本身的客观属性与实际的需要。
比如一个MyTime类
public class MyTime{
public String date;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
date= df.format(new Date());
public String getDate() {
return date;
}
//
// public void setDate(String date) {
// this.date = date;
// }
}
显然这个类是只提供了显示系统的时间,不提供外部去修改系统时间的方法。这个也是根据类本身的客观属性而言,时间是固有不会人为改变的属性,当然诸如此类的例子,还有很多。我们只是简单举例来表明我们该如何设计类的外部访问方法。
访问权限
在上面的代码中,我们已经看到可以调用**getData()**方法对date进行访问,但是date的修饰符是public ,换句话说,我们完全可以new 一个MyTime对象对date属性进行修改和读值,这就违背了我们封装的初衷。所以,Java引入了不同的访问权限来限定这件事情。在《Java编程思想》中讲解了下述的访问权限
public: 接口访问权限。也就是说public修饰的成员方法,属性都是可以在任何地方所访问的。
private: 你无法访问 。也就说private修饰的成员方法,属性 都是只能在这个类中被访问,出了这个类以外的地方,你是无法访问到这个类里面的内容,这十分的有用,我们上面的例子代码,因为是public修饰,所以会出现封装不彻底的问题,如果我们的成员变量date用private修饰,那么就会不出现上述的问题。
protected: 继承访问权限。在说到继承访问权限之前,我们思考这样一个问题,我们有一个基类,是需要被继承的。基类中的成员属性,我们是希望能被继承的类所访问,那么我们是可以把他设定成public,但是一旦这样的话,除了这个继承的类,其他类也会访问到。这个显然不是我们所期望的事情,所以Java引入protected修饰符,来表示所修饰的部分是能被派生的类所访问的,同时他也能被同一包里面的其他类所访问。
总结一下,如果我们是希望类中的方法和属性能被所有类所访问,就用public,如果只是能被继承的类和同一包内的类所访问就用protected,如果只是希望在当前类中所访问,不能被其他任何类访问就要用private。
在了解了封装的具体含义和好处之后,我们就可以利用上面的访问权限来对我们要向封装的东西进行封装,通过访问权限来隐藏具体的实现,和提供外部访问的接口。
public class Student {
private String name;
private String age;
private String handleName(String name){
return "I'm " + name;
}
private String handleAge(String age) {
return age + " 岁";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = handleName(name);
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = handleAge(age);
}
}
在上面的例子中,我们对Student类进行了简单的封装,首先我们切断了外部直接调用到name,age的可能,通过set和get方法来提供访问到name,age的方法。
同时,上面的例子中也体现出来了,在外部访问方法不变的情况下,自己随意修改内部的实现。
我们首先编写了只供类内部调用的Handle的方法,用Handle方法来修改name,age的显示结果。
这样确保了修改显示结果的方法只能自己调用,而且在外部访问不变的情况下,根据需求修改内部的实现。
这也是一个简单的封装例子。其调用的例子如下:student对象只能调用如下的内容,其他我们自己编写的内容是无法调用的。
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.setName("byhieg");
student.setAge("23");
System.out.println(student.getName() + " " + student.getAge());
}
}
包
Java 中的包package, 就是电脑中的文件夹。我们平时在工作中,文件太多时,都会新建文件夹进行分类管理,java 中的包也是类似的道理,当我们的类太多时,也需要进行分类管理,这时我们就会把类文件放到包中,就是把这个.class文件放到了一个文件夹中,这样也能有效地避免了命名冲突。
当我们对java源文件进行编译时,它会生成一个.class 文件,如果我们在java源文件的顶部,指定一个包名(package net;), 编译时,这个包名会生成一个文件夹,在这里就是net文件夹,编译好的.class文件则会放到该文件夹下。
package net; // 指定包名net
public class Test {
public static void main(String[] args) {
System.out.println("hello");
}
}
当包名很长时,如package net.com.cn,这时它就会生成多级文件夹,先生成net文件夹,然后在net文件夹中生成com文件夹,最后在com文件夹中生成cn文件夹,我们编译生成的.class 文件就会放到cn 文件夹中。我们写一个Animal 类,指定包名为net.com.cn
package net.com.cn;
public class Animal {
private String name;
private int age;
public Animal( String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
编译之后,你会发现我们当前文夹中多了一个net文件夹,打开net文件夹,发现com文件夹,再打开com文件夹,发现cn 文件夹,在cn文件里面才有我们的Animal.class 类文件。 有了包之后,这个Animal类就有了所属,这个类实际上叫做net.com.cn.Animal, 如果在java源文件中使用这个类创建对象,就要使用类的全称net.com.cn.Animal
net.com.cn.Animal cat = new net.com.cn.Animal();
这样使用类创建对象就非常麻烦了,所以出现了import导包。 把要使用的类直接导进来,我们在源代码中就直接可以使用类名进行书写。 我们写一个test.java文件
import net.com.cn.Animal; // 引入包
public class Test {
public static void main(String[] args) {
Animal cat = new Animal(); // 直接使用类名进行书写
cat.setName("miaomiao");
System.out.println(cat.getName());
}
}
当引入包之后,就是一个类文件有了所属之后,这又带来权限问题。如果包中的class类文件能够被访问,它必须是public的,包中的方法,如果能够被访问,它也必须是public. 包与包之间的类进行访问,被访问的包中的类必须是public权限的,被访问的包中的类的方法也必须是public 权限的。
最后还有一个jar包,它就是一个压缩文件,当我们编译之后,类文件都放到了文件夹中,这时 我们再对文件夹进行压缩,就形成了jar包, 就像我们在文件夹上面,点击右键,生成winrar文件 一样。因为压缩文件体积小,有利于进行传播。第三方包,就是通过 jar进行发布,我们直接导入jar包,就可以对里面的类文件进行使用。
static关键字
static关键字:
特点:
1.static是一个修饰符,用于修饰成员。(成员变量,成员函数)static修饰的成员变量 称之为静态变量或类变量。
2.static修饰的成员被所有的对象共享。
3.static优先于对象存在,因为static的成员随着类的加载就已经存在。
4.static修饰的成员多了一种调用方式,可以直接被类名所调用,(类名.静态成员)。
5.static修饰的数据是共享数据,对象中的存储的是特有的数据。
private static int age;//用static修饰的成员变量静态变量或者叫做类变量
private String name; //成员变量
public static void show(){//静态函数 可直接用类来调用
System.out.println("showStatic");
}
public void showDemo(){//成员函数 需要创建对象才可以调用
System.out.println("showDemo");
}
成员变量和静态变量的区别:
1.生命周期的不同:
成员变量随着对象的创建而存在随着对象的回收而释放。
静态变量随着类的加载而存在随着类的消失而消失。
2.调用方式不同:
成员变量只能被对象调用。
静态变量可以被对象调用,也可以用类名调用。(推荐用类名调用)
3.别名不同:
成员变量也称为实例变量。
静态变量称为类变量。
4.数据存储位置不同:
成员变量数据存储在堆内存的对象中,所以也叫对象的特有数据。
静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据。
public class Demo {
private static int age;//用static修饰的成员变量静态变量或者叫做类变量
private String name; //成员变量
public static void show(){//静态函数 可直接用类来调用
System.out.println("showStatic");
}
public void showDemo(){//成员函数 需要创建对象才可以调用
System.out.println("showDemo");
}
public static void main(String[] args) {
Demo.age = 1;//静态变量可以直接用类名调用。随着类加载而存在类的消失而消失
Demo d = new Demo();
d.age = 10;//静态变量也可以通过对象来调用(推荐用类名来调用)
d.name="张三";//成员变量只能通过对象来调用。随着对象创建而存在随着对象回收而释放。
Demo.show();//静态方法也可以直接用类名调用。
d.show(); //静态方法也可以通过对象来调用。
d.showDemo();//非静态方法只能通过对象来调用。
}
}
静态使用时需要注意的事项:
1.静态方法只能访问静态成员。(非静态既可以访问静态,又可以访问非静态)
2.静态方法中不可以使用this或者super关键字。
3.主函数是静态的。
public class Demo {
private static int age;//用static修饰的成员变量静态变量或者叫做类变量
private String name; //成员变量也叫做实例变量
public static void show(){//静态函数
Demo d = new Demo();//因为静态先于对象加载如果需要访问必须要创建对象才能访问
d.name = "张三";//静态方法不能直接访问非静态的成员变量
d.showDemo();//静态方法不能直接访问非静态的成员函数
System.out.println(d.name);
}
public void showDemo(){//成员函数
age = 10;//可以直接访问静态变量
show();//也可以直接访问静态函数
System.out.println(age);
}
}
什么时候使用static来修饰
1.静态变量:
当分析对象中所具备的成员变量的值都是相同的。这时这个成员就可以被静态修饰。
只要是数据在对象中都是不同的,就是对象的特有数据,必须存储在对象中,是非静态的。
如果是相同的数据,对象不需要做修改,只需要使用即可,不需要存储在对象中,是静态的。
2.静态函数。
函数是否用静态修饰,就参考一点,就是该函数功能是否有访问到对象中特有的数据。
简单来说,从源代码看,该功能是否需要访问非静态的成员变量,如果需要,该功能就是非静态的。如果不需要,就可以将该功能定义成静态的。当然,也可以定义成非静态,但是非静态需要被对象调用,而仅创建对象是没有意义的。
静态代码块:
随着类的调用或创建实例而执行,而且只执行一次。
作用:
用于给类进行初始化。
public class Demo {//如果想让此类成为一个静态类而类中需要用到一些参数需要初始化就需要静态代码块
private static int age;
private static String name;
//省略get、set方法
static{//当类第一次调用或创建实例时给属性初始化且只执行一次。
age = 10;
name = "张三";
}
public static void showNoen(){//调用此方法age为10name为张三(默认值)
System.out.println("年龄:"+age+"姓名:"+name);
}
public static void show(int age,String name){//调用此方法会覆盖掉默认值
Demo.age = age;
Demo.name = name;
System.out.println("年龄:"+Demo.age+"姓名:"+Demo.name);
}
public static void main(String[] args) {
Demo.showNoen();//结果为:年龄:10姓名:张三
Demo.show(50, "赵四");//结果为:年龄:50姓名:赵四
}
}
以上是封装特征和static的介绍 ,下期了解‘继承’特征,以上内容可能会有出入,部分出自转载,欢迎大家踊跃留言指正。