zoukankan      html  css  js  c++  java
  • 4.4 封装和隐藏

    一、理解封装

      封装[Encapsulation]是面向对象的三大特征(封装、继承、多态)之一,它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。

      封装的两方法面含义:把该隐藏起来的隐藏起来,把该暴露的暴露出来:
    1、把对象的成员变量和实现细节都隐藏起来,不允许外部直接访问
    2、把方法暴露出来,让方法来控制对这些成员变量进行安全访问和操作。

      封装的目的:
    1、隐藏类的实现细节。
    2、让使用者只能通过预定的方法来访问数据,从而可以在该方法里加入控制逻辑,限制堆成员变量的不合理访问。
    3、进行数据检查,从而有利于保证对象的完整性。
    4、便于修改提高程序的可维护性。

    二、使用访问控制符

      Java使用三个访问控制符:public、protected、private,分别代表3个访问控制级别,另外还有一个不加任何访问控制符的访问控制级别。4个访问级别有小到大如图所示:

    ★private(当前类访问权限):表示该成员只能在当前类的内部被访问。该访问控制符用于修饰成员变量最合适,使用它来修饰成员变量就可以把成员变量隐藏在该类的内部。
    ★default(不写,包访问权限):表示该成员或外部类就可以被相同包下的其他类访问。
    ★protected(子类访问权限):表示该成员既可以被同一个包类的其他类访问,也可以被不同包中的的子类访问。在通常情况下,使用protected来修饰一个方法,通常希望其他子类来重写这个方法。
    ★public(公共访问权限):表示该成员或外部类就可以被其他所有类访问,不管访问类和被访问类是否处于同一个包中,是否具有父子继承关系。

    private default(不写) protected public
    同一个类中
    同一个包中
    子类中
    全局范围中

      访问控制符用于控制一个类成员是否可以被其他类访问,对于局部变量而言,其作用域就是其所在的方法,不可能被其他类所访问,因此不能使用访问控制符。
      对于外部类而言,只有两种访问级别:public和默认,外部类不能使用private和protected修饰,因为外部类没有处于任何类的内部,也就没有其所在内的内部、所在类的子类两个范围,因此private和protected访问控制符对外部类没有意义

    注意:如果一个Java源文件定义的所有类都没有使用public修饰,则这个Java源文件的文件名可以是一切合法的文件名;但如果一个Java源文件里定义了一个public修饰的类,则这个源文件名必须与public修饰的类的类名同名。
    下面通过合理访问控制符定义了一个Person类:

    public class Person 
    {
    	//使用private修饰成员变量,将这些成员变量隐藏起来
    	private String name;
    	private int age;
    
    	//提供方法操作name成员变量,name只能在当前类访问
    	public void setName(String name)
    	{
    		//执行合理性检验,要求用户名必须在2-6位之间
    		if(name.length()>6||name.length()<2)
    		{
    			System.out.println("你设置的人民不符合要求");
    			return;//结束程序
    		}
    		else
    		{
    			this.name=name;
    		}
    	}
    	public String getName()
    	{
    		return this.name;
    	}
    	
    	//提供操作age成员变量
    	public void setAge(int age)
    	{
    		//用户年龄在0-100之间
    		if(age>100||age<0)
    		{
    			System.out.println("你设置的年龄不合法");
    			return;
    		}
    		else
    		{
    			this.age=age;
    		}
    	}
    	public int getAge()
    	{
    		return this.age;
    	}
    }
    

    定义上面的Person类后,该类的name和age两个成员变量只有在Person类内才可以操作和访问,在Person类之外只能通过各自对应的setter和getter方法来操作和访问他们。
      Java类里的实例变量的setter和getter方法有着非常重要的意义。如果一个Java类里的每个实例变量都是以private修饰,并为每个实例变量都提供public修饰的setter和getter方法,那么这个类就是一个符合JavaBean规范的类。因此JavaBean总是一个封装良好的类。setter和getter方法合起来就变成属性,如果只有getter方法,则是只读属性。
    下面程序示例访问Person类的成员变量

    class PersonTest
    {
    	public static void main(String[] args) 
    	{
    		var p=new Person();
    		//因为age变量已经被隐藏,所以下面语句将出现错误
    		//p.age=1000;
    		//PersonTest.java:7: 错误: age 在 Person 中是 private 访问控制
    
    		p.setAge(1000);
    		//你设置的年龄不合法,故并未对成员变量age设置成功
    		System.out.println(p.getAge());
    
    		//有效设置成员变量age后
    		p.setAge(20);
    		System.out.println(p.getAge());
    		//同理对成员变量name也只能通过setter和getter方法进行访问
    
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    你设置的年龄不合法
    0
    20
    
    输出完成 (耗时 0 秒) - 正常终止
    

    关于访问控制符的使用原则:
    1、类里的绝大部分成员变量都应该使用private修饰,只有一些static修饰的、类似全局变量的成员变量,才有可能考虑用public修饰。除此之外,有些方法只用于辅助实现该类的其他方法,这些方法被称为工具方法,工具方法也应该使用private修饰。
    2、如果某个类主要用于其他类的父类,该类里包含的大部分方法可能仅希望被其他子类重写,而不想被外界直接调用,则应该使用protected修饰这些方法。
    3、希望暴露出来给其他类自由调用方法,应该使用public修饰。因此类的构造器通常使用public修饰,从而允许在其他地方创建该类的实例。因为外部类通常希望被其他类自由调用,所以大部分外类都使用public修饰。

    三、package、import和import static

    3.1 package包引用

    Oracle也允许在类名前增加一个前缀来限定这个类。Java引入包机制,提供类的多层命名空间,用于解决类的命名冲突、类文件管理的冲突。
    Java允许将一组功能相关的类,放到同一个package下。如果希望把一个类放到指定的包结构下,应该在Java源程序的第一个非注释行放置如下格式代码:
    package packageName;
    一旦在Java源文件中使用了package语句,就意味者该源文件里定义的所有类都属于这个包。位于包中的每个类的完整类名都应该是包名和类名的组合,如果其他人使用该包下的类,也应该使用包名加类名的组合。
    下面在lee包定义一个简单的Java类

    package lee;
    public class Hello 
    {
    	public static void main(String[] args) 
    	{
    		System.out.println("Hello World!");
    	}
    }
    

    Hello.java源文件存储在:

    进入Hello.java的源文件的路径下,在该路径下用javac来编译这个源文件。

    -d选项用于设置编译生成的class文件的保存位置,这里指定将生成的class文件放在当前路径下(.就代表当前路径)。使用该命令后,发现当前路径下并没有class文件,而是在当前路径下多了一个lee文件夹,文件夹里有一个Hello.class文件。

    package这样的用法是为了,避免同名类引起的冲突。Java规定:位于包中的类,在文件系统中也必须有与包名层次相同的目录结果。所以对于上面的Hello.calss它必须放在lee文件夹中才有效
    注意:如果直接使用javac Hello.java命令来编译这个文件,将会在当前路径下生成一个Hello.class文件,而不会生成lee文件。也就是说,如果编译Java文件不使用-d选项,编译器不会为Java源文件生成相同的目录结果。鉴于此,推荐编译Java源文件时总是使用-d选项,即使想把生成的calss文件放在当前路径下,也应该使用-d .选项,
    进入编译器生成的lee文件夹所在路径,执行如下命令
    java lee.Hello
    将看到程序正常输出:

    E:Java第五章 面向对象5.4 隐藏和封装>java lee.Hello
    Hello World!
    

    当时如果直接进入lee文件下.class文件所在路径下执行java Hello命令来运行Hello类,系统将出现错误。

    当虚拟机要加载lee.Hello类时,它会依次搜索CLASSPATH环境变量所指定的系列路径,查找这些路径下是否包含lee路径,并在lee路径下查找是否包含Hello.java文件。虚拟机在装载带包名的类时,会先搜索CLASSPATH环境变量指定的目录,然后在这些目录中按与包层次对应的目录结构去查找class文件。
    注:将源文件和class文件统一存放,有可能造成混乱,通常建议将源文件和class文件分开存放,以便管理。
    Java包机制需要两方面的保证:(1)源文件使用package语句指定包名;(2)class文件必须放在对应的路径下

    3.2 import省略写类名

    我们再在lee包的子包sub下定义一个Apple类

    package lee.sub;
    public class Apple 
    {
    	public static void show() 
    	{
    		System.out.println("这是一个Aplle类");
    	}
    }
    

    这里在lee文件下定义一个Java源文件对Hello类和Apple类进行调用:

    class PackageTest 
    {
    	//调用lee文件夹下的Hello类
    	public static void main(String[] args) 
    	{
    		var h=new lee.Hello();
    		var a=new lee.sub.Apple();
    		a.show();
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    这是一个Aplle类
    
    输出完成 (耗时 0 秒) - 正常终止
    

    可以看出,创建其他包下的实例,则在调用构造器时也必须使用包的前缀,例如lee.Hello()、lee.sub.show()。为了简化编程,省略写包名,Java引入了import关键字,import可以向某个Java文件中导入指定包层次下的某个类或全部类,import语句应该出现在package语句之后,类定义之前。
    import导入类的语句:

    import package.subpackage...classname;//导入单个类
    import package.subpackage...*;//导入指定包下的全部类
    

    一旦在Java源文件使用import语句来导入指定类,在该源文件中使用这些类就可以使用包前缀,不在需要类全名。使用import语句简化上面的PackageTest源程序

    import lee.Hello;
    import lee.sub.Apple;
    class PackageTestplus 
    {
    	//调用lee文件夹下的Hello类
    	public static void main(String[] args) 
    	{
    		var h=new Hello();
    		var a=new Apple();
    		a.show();
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    这是一个Aplle类
    
    输出完成 (耗时 0 秒) - 正常终止
    

    关键点:
    ★Java默认为所有源文件导入java.lang(lang是language的简写,保存的是语法相关的语句)包下的所有类,因此前面在Java程序中使用String、System类时都无须使用import语句导入这些类。
    ★在一些特殊情况下也必须使用类全名:
    例如在同一个程序中使用java.sql包下的类和java.util下的类,则可以使用下面两行import语句:

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

    如果在接下来的程序中需要使用Date类,则会引起编译错误,原因在于java.sql和java.util包下都包含Date类,引起了系统混乱。在这种情况下需要使用类全名,如:

    java.sql.Date d=new java.sql.Date();
    

    3.3 import static静态导入

    JDK1.5以后增加了一种静态导入的语法,它用于导入指定类的某个静态成员变量、方法或全部的静态成员变量或方法。
    导入指定类的单个静态成员变量、方法的语法格式:

    import static package.subpackage...ClassName.FieldName|MethodName;
    

    导入指定类的全部静态成员变量、方法的语法格式:

    import static package.subpackage...ClassName.*;
    

    所谓静态成员变量、静态方法就是类变量和类方法,他们都需要static修饰。使用import可以省略包名;import static则可以连类名都可以省略
    下面的程序使用import static语句来导入java.lang.System类下的全部静态成员变量,从而可以将程序简化为:

    import static java.lang.System.*;
    import static java.lang.Math.*;
    class StaticImportTest 
    {
    	public static void main(String[] args) 
    	{
    		//out是java.lang.System类的静态方法,代表标准输出
    		//PI是Math类的静态成员变量,代表圆周率
    		System.out.println("Hello World!");
    		out.println("Hello World");
    		System.out.println(Math.PI);
    		out.println(PI);
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    Hello World!
    Hello World
    3.141592653589793
    3.141592653589793
    
    输出完成 (耗时 0 秒) - 正常终止
    

    3.4 java常用包

    Java核心类都放在java包以及其子包下,java扩展的许多类都放在javax包以及其子包下。
    Java语言常用的几个核心库:
    ★java.lang包。
    该包提供了Java语言进行程序设计的基础类,它是默认导入的包。该包里面的Runnable接口和Object、Math、String、StringBuffer、System、Thread以及Throwable类需要重点掌握,因为它们应用很广。
    ★java.util包。
    该包提供了包含集合框架、遗留的集合类、事件模型、日期和时间实施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。
    ★java.io包。
    该包通过文件系统、数据流和序列化提供系统的输入与输出。
    ★java.net包。
    该包提供实现网络应用与开发的类。
    ★java.sql包。
    该包提供了使用Java语言访问并处理存储在数据源(通常是一个关系型数据库)中的数据API。
    ★java.awt包
    该包包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
    ★javax.swing包。
    这两个包提供了GUI设计与开发的类。
    java.awt包提供了创建界面和绘制图形图像的所有类,而javax.swing包提供了一组“轻量级”的组件,尽量让这些组件在所有平台上的工作方式相同。

  • 相关阅读:
    java 枚举类小结 Enum
    hibernate查询
    java装饰者模式理解
    内部类学习
    java使用json将HashMap转化成javabean小例子
    HashMap存储数据赋值javabean简单示例
    java数组转化成集合
    java正则匹配并提取字串
    Windows cmd命令反斜杠问题
    自动化构建工具
  • 原文地址:https://www.cnblogs.com/weststar/p/12356262.html
Copyright © 2011-2022 走看看