[读书笔记]Java编程思想第6章之访问权限控制

Posted Spring-_-Bear

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[读书笔记]Java编程思想第6章之访问权限控制相关的知识,希望对你有一定的参考价值。

  1. 当编写一个Java源代码文件时,此文件通常被称为编译单元。每一个编译单元都必须有一个后缀名.java,而在编译单元内有一个public类,该类的名称必须与文件的名称相同。
  2. Java可运行程序是一组可以打包并压缩为一个Java文档文件(JAR,使用Java的jar文档生成器)的.class文件。Java解释器负责这些文件的查找、装载和解释。
  3. Java包的名称命名规则全部使用小写字母。
  4. 既然构造器是唯一定义的构造器,并且它是private的,那么它将阻止对此类的继承。
package thinkinjava.charpenter6;

/**
 * @author Spring-_-Bear
 * @version 2021/9/24 16:24
 */
public class IceCream {
    public static void main(String[] args) {
        // ! Sundae x = new Sundae();
        Sundae x = Sundae.makeASundae();
    }
}

class Sundae{
    private Sundae(){
        System.out.println("Sundae();");
    }

    static Sundae makeASundae(){
        return new Sundae();
    }
}
  1. 虽然不是很常用,但编译单元内完全不带public类也是可能的。在这种情况下,可以随意对文件命名。
  2. 类既不可以是private的(这样会使得处该类之外,任何其他类都不可以访问它),也不可以是protected的。所以对于类的访问权限,仅有两个选择:包访问权限或public。如果不希望其它任何人对该类拥有访问权限,可以把所有的构造器都指定为private,从而阻止任何人创建该类的对象,但有一个例外,就是你在该类的static成员内部可以创建。注意:一个内部类可以是private或是protected的,但那是一个特例。
package thinkinjava.charpenter6;

/**
 * @author Spring-_-Bear
 * @version 2021/9/24 16:42
 */
public class Lunch {
    public static void main(String[] args) {
        Soup1.makeSoup();
        Soup2.access();
    }
}

class Soup1 {
    /* Allow creation via static method */
    private Soup1() {
        System.out.println("Soup1();");
    }

    public static Soup1 makeSoup() {
        return new Soup1();
    }
}

class Soup2 {
    /* Allow creation via a static object and return a reference */
    private Soup2() {
        System.out.println("Soup2();");
    }

    private static Soup2 ps1 = new Soup2();

    public static Soup2 access() {
        return ps1;
    }
}

以上的例子给出了两种选择:在Soup1中,创建一个static方法,它创建了一个新的Soup1对象并返回一个对它的引用。如果想要在返回引用之前在Soup1上做一些额外的工作,或是如果想要记录到底创建了多少个Soup1对象(可能要限制其数量);Soup2类的对象是作为Soup2的一个static privaate成员而创建的,所以有且仅有一个,而且只能通过access()方法访问到它。
练习1: 在某个包中创建一个类,在这个类所处的包的外部创建该类的一个实例。

/* In another directory:
* /access/mypackage/MyPackagedClass.java
* The most important thing is to add environment variables to the operating system.
* package access.mypackage;
*
* public class MyPackagedClass {
*	public MyPackagedClass() {System.out.println("MyPackagedClass()");}
* }	
*/

public class UnpackagedMyClass {
	public static void main(String[] args) {
		access.mypackage.MyPackagedClass m = new access.mypackage.MyPackagedClass();
	}
}

练习2: 将本节中的代码片段改写为完整的程序,并校验实际所发生的冲突。

import net.mindview.simple.*;
import java.util.*;
 
public class Collision {
	public static void main(String[] args) {
		// Vector v = new Vector(); // ambiquous collision
		net.mindview.simple.Vector v1 = new net.mindview.simple.Vector();
		java.util.Vector v2 = new java.util.Vector();
	}
}

练习3: 创建两个包:debug和debugoff,它们都包含一个相同的类,该类有一个debug()方法。第一个版本显示发送给控制台的String参数,而第二个版本什么也不做。使用静态import语句将该类导入到一个测试程序中,并示范条件编译效果。

/* In directory access/debugoff:
* // access/debugoff/Debug.java
* package access.debugoff;
*
* public class Debug {
*	public static void debug(String s) { }
* }
*/

package access.debug;

public class Debug {
	public static void debug(String s) {
		System.out.println(s);
	}
}

练习4: 展示protected方法具有包访问权限,但是它仍旧不是public的。

package access.cookie2;

public class CookieMonster {
	public static void main(String[] args) {
		Cookie x = new Cookie();
		x.bite(); // package access to protected method
	}	
}
import access.cookie2.*;

public class CookieThief {
	public static void main(String[] args) {
		Cookie x = new Cookie();
		//! x.bite(); // access protected
	}	
}

练习5: 创建一个带有public,private,protected和包访问权限域以及方法成员的类。创建该类的一个对象,看看在你试图调用所有类成员时,会得到什么类型的编译信息。请注意,处于同一个目录中的所有类都是默认包的一部分。

/* in same directory:
* package access; 
*
* public class FourWays {
*	int a = 0;
*	public int b = 1;
*	protected int c = 2;
*	private int d = 3;
*	FourWays() { System.out.println("FourWays() constructor"); }
*	void showa() { System.out.println(a); }
*	public void showb() { System.out.println(b); }
*	protected void showc() { System.out.println(c); }
*	private void showd() { System.out.println(d); }	
* }
*/

package access; 

public class AccessTest {
	public static void main(String[] args) {
		FourWays fw = new FourWays();
		fw.showa();
		fw.showb();
		fw.showc();
		fw.a = 10;
		fw.b = 20;
		fw.c = 30;
		fw.showa();
		fw.showb();
		fw.showc();
		//! fw.showd(); // private access, compiler can't touch
	}	
}

练习6: 创建一个带有protected数据的类。运用第一个类中处理protected数据的方法在相同的文件中创建第二个类。

class SomeData {
	protected int a = 13;
}

class DataChanger {
	static void change(SomeData sd, int i) { sd.a = i; }
}

public class ProtectedData {
	public static void main(String[] args) {
		SomeData x = new SomeData();
		System.out.println(x.a);					
		DataChanger.change(x, 99);
		System.out.println(x.a);		
	}	
}

练习7: 根据描述access和Widget的代码片段创建类库。在某个不属于access类库的类中创建一个Widget实例。

/* in access package:
* // access/Widget.java
* package access;
*
* public class Widget {
* 	public Widget() { System.out.println("Widget()"); }
* }
*/

import access.*;

public class MakeWidget {	
	public static void main(String[] args) {
		Widget w = new Widget();
	}
}

练习8: 效仿示例Lunch.java的形式,创建一个名为ConnectionManager的类,该类管理一个元素为Connection对象的固定数组。客户端程序员不能直接创建Connection对象,而只能通过ConnectionManager中的某个static方法来获取它们。当ConnectionManaget之中不再有对象时,它会返回null引用。在main()之中检测这些类。

package thinkinjava.charpenter6;

import java.util.Random;

/**
 * @author Spring-_-Bear
 * @version 2021/9/25 17:25
 */
public class ConnectionManager {
    static int leftObject = 5;
    static Connection[] connections = new Connection[leftObject];

    /* Initial the object's reference */
    static {
        for (Connection c : connections) {
            c = Connection.makeConnection();
        }
    }

    /* If the number of objects is greater than zero, return its reference,
     * otherwise return null
     */
    public Connection getConnection() {
        if (leftObject > 0) {
            return connections[--leftObject];
        } else {
            System.out.println("NO MORE CONNECTIONS!");
            return null;
        }
    }

    public static void main(String[] args) {
        ConnectionManager connectionManager = new ConnectionManager();
        Random random = new Random(47);
        int randomNum = random.nextInt(10);

        for (int i = 0; i < randomNum; i++) {
            System.out.println("The number of left objects: " + leftObject);
            connectionManager.getConnection();
        }
    }

}

class Connection {
    static int cnt = 0;

    /* avoid directly creating the object of Connection */
    private Connection() {
        cnt++;
        System.out.println("Connection(): " + cnt);
    }

    /* create a method to create the object of Connection
     *  and return the reference of the object
     */
    static Connection makeConnection() {
        return new Connection();
    }
}

练习9: 在access/local目录下编写以下问价(假定access/local在你的CLASSPATH中):

package access.local;

class PackagedClass{
    public PackagedClass(){
        System.out.println("Creating a packaged class.")
    }
}

然后在access/local之外的另一目录下创建下列文件:

package access.foreign;
import access.local.*;

public class Foreign{
    public static void main(String[] args){
        PackagedClass pc = new PackagedClass();
    }
}

解释一下为什么编译器会产生错误。如果将Foreign置于access.local包之中的话,会有所改变吗?

/* Compiler Error Reason: PackagedClass in not public, so no access outside of package. Moving Foreign to local would allow package access to PackagedClass.
*/

以上是关于[读书笔记]Java编程思想第6章之访问权限控制的主要内容,如果未能解决你的问题,请参考以下文章

Java编程思想读书笔记_第6章(访问权限)

[读书笔记]Java编程思想第3章之操作符

[读书笔记]Java编程思想第8章之多态

[读书笔记]Java编程思想第9章之接口

[读书笔记]Java编程思想第7章之复用类

[读书笔记]Java编程思想第5章之初始化与清理