Java之封装与访问权限控制

Java之封装与访问权限控制(一)

对于封装的概念,我总觉得自己还是挺了解的,但是真要我说,还真说不出个啥来。我只能默默地通过身边的例子加上书本理论完善我对封装的认识。

就比如,我们在玩游戏的时候,我们只能通过完成指定任务获得金币,并不能直接修改金币的值,作为玩家的我们,如果轻易就能修改机密,那岂不是乱套啦。设计者明显不想让我们这么做,他们允许我们享受游戏,但是这些禁忌碰不得。这就是封装的一个例子。

封装的概念

封装是面向对象三大特征之一。
将对象的状态信息隐藏再对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法对内部信息进行操作和访问。

优点

  • 隐藏类的实现细节。
  • 使用者只能通过预定的方法访问数据,可以控制方法逻辑,限制不合理访问。
  • 可进行数据检查,利于保证对象信息的完整性。
  • 便于修改,提高代码的可维护性。

需要考虑

  • 将对象的成员变量和实现细节隐藏起来,不允许外部直接访问。
  • 把方法暴露出来,让方法来控制对这些成员变量进行安全的访问操作。

将该隐藏的隐藏起来,将该暴露的暴露出来。

访问控制符

控制级别
public > protected >缺省> private

访问控制级别表

privatedefaultprotectedpublic
同一个类中
同一个包中
子类中
全局范围内

private(当前类访问权限):被修饰的成员只能在该类的内部被访问。

缺省(包访问权限) :缺省就是没有任何修饰符所修饰,缺省的成员可以被同一个包中的其他类所访问,关于包的概念,之后再提。

protected(子类访问权限) :被修饰的成员既可以被同一个包中的其他类访问,也可以被不同包中的子类所访问。关于子类和父类之后将会总结~

public(公共访问权限) :被修饰的成员可以被其他所有类访问,不论是否在同一包,不论是否具有继承关系。

注意

  • 外部类只能由public或缺省两种修饰方式,其他两个修饰没啥太大意义。

  • Java源文件的命名问题:

    • 定义的所有类中没有用public修饰,文件名随意取,合法就行,但不建议这样。
    • 如果定义了public修饰的类,文件名必须与public修饰的类类名相同,所以一个java源文件中只能有一个public修饰的类。
  • private用来修饰成员变量非常合适,可以很好实现隐藏和封装的目的。

  • 通常来说,用protected修饰一个方法,是希望子类来重写这个方法。

属性私有化

先来看看我们原先写过的简单的类及测试:

package com.my.pac08;

public class People {
    public static void main(String[] args) {
        Man man = new Man();
        man.age = -4;
        man.name = "12345";
        man.run();
    }
}

class Man {
    int age;
    String name;

    void run() {
        System.out.println("running..");
    }
}

  • 一切都是那么普通,可以不加修饰符的地方都没有加。
  • 我们还是普通地创建了对象,普通地通过对象访问其属性。
  • 但是这样子会产生一个很明显的问题:要是和上面一样,赋上很离谱的值,就会产生同样离谱的结果
  • 于是我们采用了以下的解决办法。
package com.my.pac07;

public class Person {
    //private 修饰符对成员变量 进行隐藏
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (name.length() > 5) {
            System.out.println(name+"的长度太长,取名失败!");
            return;
        }
        System.out.println("取名成功!");
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age < 0) {
            System.out.println("输入年龄不合法!");
            return;
        }
        this.age = age;
    }
}

  • 将name和age两个实例变量用private修饰。
  • 加上与之匹配的一组方法,setter与getter方法用来设置与获取属性。
  • 在设置方法处,加入了逻辑判断,限制非法或无效赋值。
package com.my.pac07;

public class PersonTest {
    public static void main(String[] args) {
        Person p = new Person();

        /*private修饰属性,不在同一个类中无法直接访问,需要
          使用对应的getter和setter方法。
          错误 p.name = 5;
          错误 p.age = 10;
         */

        //名字长度超过限制,通过加入逻辑控制输入
        p.setName("Longname");

        System.out.println(p.getName());
        //赋符合标准的名字
        p.setName("Dady");

        System.out.println("p的名字是:"+p.getName());
        //年龄超出限制,不能小于0
        p.setAge(-4);

        System.out.println(p.getAge());
        //赋正常年龄值
        p.setAge(10);

        System.out.println("p的年龄是:"+p.getAge());
    }
}

  • 无法再用对象.属性的方式直接访问。
  • 需要通过setter方法设置合理值,用getter方法获取值。
  • 两个方法的形式:set+首字母大写的属性,例如setName。getter方法同理。

以上简单的例子,就是私有化属性,隐藏需要隐藏的,并提供可以访问属性的方法,展示需要展示的,这就是封装性的体现之一。

Java之封装与访问权限控制(二)

访问权限控制是具体实现的隐藏,是封装性的一部分体现。前面提到几个访问控制修饰符,是访问权限控制的一部分。接下来要探讨这块另一个重要的概念,包(package)

包:库单元

包解决了什么问题?

Java作为面向对象程序设计语言,"高内聚,低耦合"是设计的目标。既然这样,如何能做到高内聚,如何有效管理这些内聚的构件,包就是充当这一角色。包机制提供了类的多层命名空间(类似于C++中的命名空间namespace),很好解决了类命名冲突及类文件管理的问题。可以说,包确保了类名的唯一性

Java编译:

  • 在编译一个.java文件时,其中每个类都会有一个输出文件。
  • 输出文件名和对应类地文件名相同,只是多了个后缀名.class
  • Java的可运行程序是一组可以打包并压缩为一个Java文档文件(JAR)的.class文件。
  • Java解释器负责查找、装载和解释这些文件。

如何理解呢?类库实际就是一组类文件,每个文件中都有一个public类和若干个非public类,所以每个文件都有一个构件,package可以让这些构件从属于同一个群组。

package语句必须是除注释以外的第一句程序代码:package+包名。包名格式是一串由.分隔的小写英文单词,为了取一个独一无二的包名,一般以域名(显然独一无二)逆序作为包名。

值得注意的是,在给定包名时就已经隐含地指定了目录结构。

Java解释器的运行过程:

  • 在环境变量CLASSPATH中查找.class文件的根目录。
  • 从根目录开始,将包名中的.替换成反斜杠(依据操作系统不同而不同),得到路径。
  • 将路径与CLASSPATH中的各个不同的项相连。
  • 在这些目录中查找.class文件。

但是需要注意:编译器在编译源文件的时候不会检查目录结构,也就是说,如果源文件没有在指定的目录下,编译不会出现错误,但是无法运行程序,因为包与目录不匹配,虚拟机找不到对应的类。

  • 建议将.java源文件和.class文件分开存放,利于管理。
  • 如果没有显示指定package语句,则处在默认包下,但是不建议。
  • 同一个包下的类可以自由访问,但是假如在com再创建一个sub子包,那么这时处在两个包下的类是不能直接访问的,而需要带上类的全名(包名+类名),也就是说,嵌套的包之间毫无关系,每个都拥有独立的集合。

说实话,关于包这部分还是有些稀里糊涂,等待后续补充~

import

不同包之间的类相互访问时,为了解决每次都需要带上类的全名的繁杂难题,import应运而生。

  • import语句需要出现在package语句之后,类定义之前。
  • import可以向某个Java文件中导入指定包层次下某个类或全部类。
    • 假如现在想导入com.my.pac06包下的Overload类:import com.my.pac06.Overload;
    • 假如想导入还是这个包中的所有类(是类!不是包!):import com.my.pac06.*;
  • import导入类之后,在使用类时就可以省略包前缀(包名),直接用类名。

特殊情况:

  • 如果两个包中含有名字相同的类,且这两个包都要用到,例如java.sql中和java.util中都有Date类,我们在同时导入时,系统就不知道该怎么办了。这时就需要重新使用类的全名java.sql.Date date = new java.sql.Date(6);,没办法,import也救不了。
package com.my.pac08;

import java.sql.*;
import java.util.*;

public class Tesr {
    /*Reference to'Date' is ambiguous,both
    * 'java.sql.Date'and'java.util.Date'match*/
//    Date date = new Date();
    java.sql.Date date = new java.sql.Date(6);
}

  • Java默认为所有源文件导入java.lang包下的所有类,我们常用的String和System类再用的时候就没有需要import导入的情况。

import static

静态导入,与import功能类似,不同在于import static用于导入指定类的某个静态成员变量、方法或全部的静态成员变量、方法。

package com.my.pac08;
//静态导入 java.lang.System类的静态成员变量out
import static java.lang.System.out;
//同理的,导入所有静态成员变量
//import static java.lang.System.*;
public class Test {
    public static void main(String[] args) {
    //静态导入之后,可以直接省略类名
        out.println("Hello,World!");
    }
}

Java常用包

Java的核心类位于java包及其子包下。
Java扩展的很多类位于javax包及其子包下。

以下罗列Java常用包:

  • java.lang:包含Java许多核心类,诸如String,Math,System,Thread。无需import导入,因为系统自动导入该包。

  • java.util:Java大量工具类/接口和集合框架类/接口,诸如Arrays,List,Set等。

  • java.net:包含Java网络编程相关类/接口。

  • java.io:包含Java输入输出编程相关的类/接口。

  • java.text:包含Java格式化相关的类。

  • java.sql:包含Java进行JDBC数据库编程的相关类/接口。

参考书籍:《疯狂Java讲义》、《Java编程思想》、《Java核心技术卷I》

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值