Java复习第五天------基础夯实之路(Java基础)

4 篇文章 0 订阅
4 篇文章 0 订阅

Java复习第五天

前几天电脑拿去修了,今天开始更新。

第十四章 类型信息

1.Java如何让我们在运行时识别对象和类的信息的。主要有两种方式,一是:传统的RTTI(在运行时,识别一个对象的类型),二是反射机制。
2.类是程序的一部分,每个类都有一个class对象,每当编写了并且编译了一个新类,就会产生一个Class对象,准确的说,是被保存到一个.class文件中。为了生成这个类的对象,JVM将使用被称为“类加载器”的子系统。类加载器子系统实际上可以包含一个类加载器链,但是只有一个原生的类加载器,它是JVM实现的一部分。原生类加载器加载的是所谓的可信类,包括JavaAPI类,它们通常是从本地盘加载的。在这条链中,通常不需要添加额外的类加载器,但是如果你有特殊需求(例如,以某种特殊的方式加载类,以来支持Web服务器应用或者在网络中下载类),那么你有一种方式可以挂接额外的类加载器。
3.所有的类都是在对其第一次使用的时候,动态地加载到JVM中的。当程序创建第一个对类的静态成员的引用时,就会加载这个类。这个也可以证明构造器也是类的静态方法,即使它没有显式的static关键字。因此,new关键字也会当做对类的静态成员的引用。
4.所有的Class对象都属于Class类。
5.Class.forName()就是为了获取某个类的Class对象的引用,它接受一个String类型的参数,返回一个Class对象的引用,对这个方法的调用,主要是为了它的副作用,当这个类没有被加载时加载这个类,在加载的过程中,这个类的static子句就会执行。如果找不到这个类就会抛出ClassNotFoundException。注意,这个方法的参数字符串,必须是要加载类的全限定类名(包含包名)。
6.无论何时,只要你想在运行时使用类型信息,就必须首先获取这个类的Class对象,Class.forName()就是实现此功能的便捷途径。但是,如果你现在有这个类的对象,那你就可以通过调用这个对象的getClass方法来获取该类的Class对象引用,这个方法也属于Object的一部分,它返回该对象的实际类型的Class对象引用。
7.类对象的getSimpleName方法返回该类的简单类名(不包含包名),getCanonicalName返回该类的全限定类名,getInterfaces返回的是Class对象,包含这个类的所包含的接口。getSuperClass返回该类的基类。
8.Class的newInstance方法是实现“虚拟构造器”的一种途径,这个方法会返回Object引用,指向实际对象,调用默认构造器。
9.Java还提供了另外一种方式来生成对Class对象的引用:使用类字面常量。比方说FancyToy.Class,这样做不仅简单,而且更安全,因为它在编译时期就会受到检查(因此不需要置于Try块中),并且它根除了对forName方法的调用,所以也就更高效。类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型。另外,对于基本类型的包装器类型,还有一个标准字段TYPE。TYPE字段是一个引用,指向对应的基本类型数据的Class对象。例如boolean.class等价于Boolean.type。建议使用前者,以便与普通类保持一致。
10.当使用类字面常量来创建类对象引用的时候,不会自动初始化该Class对象。
11.为了使用类而做的准备工作实际包含三步。
(1)加载,这是类加载器执行的。该步骤是查找字节码,并从这些字节码中创建一个Class对象。
(2)链接。在链接阶段将验证类中的字节码,为静态域分配存储空间,如果必要的话,将解析这个类创建的对其他类的所有引用。
(3)初始化(父类)。如果该类有超类,那么对其初始化,执行静态初始化器和静态初始化块。
而初始化被延迟到了对静态方法或者非常数静态域的首次引用才执行。
Class.forName立即进行了初始化。
如果一个static final值是“编译时常量”,那么不需要对该类进行初始化就可以读取,但是如果只是将一个域设置成static final,还不足以确保这种行为,因为它可能不是编译时常量,对这样的常量的访问将强制进行类的初始化。如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,要先进行链接(为这个域分配存储空间)和初始化(初始化存储空间)。
12.

public class GenericClassReferences {
	public static void main(String[] args) {
		Class intClass = int.class;
		Class<Integer> genericIntClass = int.class;
		genericIntClass = Integer.class; // Same thing
		intClass = double.class;
		// genericIntClass = double.class; // Illegal
}
} ///:~

泛型类引用只能赋值为指向其声明的类型,但是普通的类引用可以被重新赋值为任何类型。
13.Class<Number> genericNumberClass = int.class;
This would seem to make sense because Integer is inherited from Number. But this
doesn’t work, because the Integer Class object is not a subclass of the Number Class
object (this may seem like a subtle distinction; we’ll look into it more deeply in the Generics
chapter).
14.泛型中的通配符“?”,表示“任何事物”。可以和extends结合,来创建一个范围,来表示某个类或者某个类的任何子类。在Class引用中添加泛型,主要是为了增加编译器检查。
15.newInstance方法将调用默认无参数构造器,如果没有的话,就会抛出异常,但是编译器并不会产生任何警告信息。
16.An interesting thing happens when you use the generic syntax for Class objects:
newlnstance( ) will return the exact type of the object, rather than just a basic Object as
you saw in ToyTest.java. This is somewhat limited:

public class GenericToyTest {
	public static void main(String[] args) throws Exception {
		Class<FancyToy> ftClass = FancyToy.class;
		// Produces exact type:
		FancyToy fancyToy = ftClass.newInstance();
		Class<? super FancyToy> up = ftClass.getSuperclass();
		// This won’t compile:
		// Class<Toy> up2 = ftClass.getSuperclass();
		// Only produces Object:
		Object obj = up.newInstance();
}
} ///:~

If you get the superclass, the compiler will only allow you to say that the superclass reference
is “some class that is a superclass of FancyToy” as seen in the expression Class <? super
FancyToy >. It will not accept a declaration of Class. This seems a bit strange
because getSuperclass( ) returns the base class (not interface) and the compiler knows
what that class is at compile time—in this case, Toy.class, not just “some superclass of
FancyToy.” In any event, because of the vagueness, the return value of up.newlnstance( )
is not a precise type, but just an Object
17.在进行向下转型的之前,使用instanceof方法就是很必要的。
18.人们想要在运行时获取类的信息的另一个动机,便是希望提供在跨网络的远程平台上创建和运行对象的能力,这种被称为远程方法调用(RMI),它允许一个Java程序将对象分布到多台机器上。
19.Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包含了Filed、Method以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行时创建的,用来表示未知类里对应的成员。这样你就可以 使用Constructor创建新的对象,用get()和set()方法读取和修改与Field对象关联的字段,用invoke方法调用与Method对象相关的方法。另外,还可以调用getFileds、getMethods和getConstructors等方法,以返回表示字段、方法以及构造器的对象的数组。
20.RTTI和反射之间的真正的区别在于:对于RTTI,是在编译时打开和检查.class文件,而反射则是在运行时打开和检查.class文件。
21.通过调用静态方法Proxy.newProxyInstance()可以创建动态代理,这个方法需要得到一个类加载器(你通常可以从已经被加载的对象中获取其类加载器,然后传递给它),一个你希望该代理实现的接口列表(不是类或抽象类),以及InvocationHandler接口的一个实现。动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器的构造器传递一个“实际”对象的引用,从而使得调用处理器在执行其中介任务时,可以将请求转发。

//: typeinfo/SimpleDynamicProxy.java
import java.lang.reflect.*;

class DynamicProxyHandler implements InvocationHandler {
	private Object proxied;

	public DynamicProxyHandler(Object proxied) {
		this.proxied = proxied;
	}

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("**** proxy: " + proxy.getClass() + ", method: " + method + ", args: " + args);
		if (args != null)
			for (Object arg : args)
				System.out.println(" " + arg);
		return method.invoke(proxied, args);
	}
}

class SimpleDynamicProxy {
	public static void consumer(Interface iface) {
		iface.doSomething();
		iface.somethingElse("bonobo");
	}

	public static void main(String[] args) {
		RealObject real = new RealObject();
		consumer(real);
// Insert a proxy and call again:
		Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(),
				new Class[] { Interface.class }, new DynamicProxyHandler(real));
		consumer(proxy);
	}
} /*
	 * Output: (95% match) doSomething somethingElse bonobo proxy: class $Proxy0,
	 * method: public abstract void Interface.doSomething(), args: null doSomething
	 **** proxy: class $Proxy0, method: public abstract void
	 * Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@42e816
	 * bonobo somethingElse bonobo
	 */// :~

22.In general you will perform the proxied operation and then use Method.invoke( ) to
forward the request to the proxied object, passing the necessary arguments. This may initially
seem limiting, as if you can only perform generic operations. However, you can filter for
certain method calls, while passing others through:

//: typeinfo/SelectingMethods.java
// Looking for particular methods in a dynamic proxy.
import java.lang.reflect.*;
import static net.mindview.util.Print.*;

class MethodSelector implements InvocationHandler {
	private Object proxied;

	public MethodSelector(Object proxied) {
		this.proxied = proxied;
	}

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if (method.getName().equals("interesting"))
			print("Proxy detected the interesting method");
		return method.invoke(proxied, args);
	}
}

interface SomeMethods {
	void boring1();

	void boring2();

	void interesting(String arg);

	void boring3();
}

class Implementation implements SomeMethods {
	public void boring1() {
		print("boring1");
	}

	public void boring2() {
		print("boring2");
	}

	public void interesting(String arg) {
		print("interesting " + arg);
	}

	public void boring3() {
		print("boring3");
	}
}

class SelectingMethods {
	public static void main(String[] args) {
		SomeMethods proxy = (SomeMethods) Proxy.newProxyInstance(SomeMethods.class.getClassLoader(),
				new Class[] { SomeMethods.class }, new MethodSelector(new Implementation()));
		proxy.boring1();
		proxy.boring2();
		proxy.interesting("bonobo");
		proxy.boring3();
	}
} /*
	 * Output: boring1 boring2 Proxy detected the interesting method interesting
	 * bonobo boring3
	 */// :~

23.通过反射,可以到达和调用所有方法,甚至是private方法。对于域来说也是如此。

第十五章 泛型

1.The ultimate goal is to give you a clear understanding of where the boundaries lie, because my experience is that by understanding the boundaries, you become a more powerful programmer. By knowing what you can’t do, you can make better use of what you can do (partly because you don’t waste time bumping up against walls).
2.下面这个类称为元组(二维的元组),它是将一组对象直接打包存储于其中的一个单一对象,这个容器对象允许读取其中元素,但不允许向其中存放新的对象。(这个概念也被称为数据传送对象或者信使)。

//: net/mindview/util/TwoTuple.java
package net.mindview.util;

public class TwoTuple<A, B> {
	public final A first;
	public final B second;

	public TwoTuple(A a, B b) {
		first = a;
		second = b;
	}

	public String toString() {
		return "(" + first + ", " + second + ")";
	}
} /// :~

3.泛型也可以应用于接口,比方说“生成器”(generator),这是一种专门负责创建对象的类。
4.Java泛型的一个局限性就是:基本类型无法作为类型参数。不过JavaSE5的自动装箱和自动拆箱功能可以将基本类型和其包装类型进行自动转换。

//: generics/Fibonacci.java
// Generate a Fibonacci sequence.
import net.mindview.util.*;

public class Fibonacci implements Generator<Integer> {
	private int count = 0;

	public Integer next() {
		return fib(count++);
	}

	private int fib(int n) {
		if (n < 2)
			return 1;
		return fib(n - 2) + fib(n - 1);
	}

	public static void main(String[] args) {
		Fibonacci gen = new Fibonacci();
		for (int i = 0; i < 18; i++)
			System.out.print(gen.next() + " ");
	}
} /*
	 * Output: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
	 */// :~

5.如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,因为他可以使事情更清楚明白。
6.泛型方法与可变参数列表能够很好地共存。

//: generics/GenericVarargs.java
import java.util.*;

public class GenericVarargs {
	public static <T> List<T> makeList(T... args) {
		List<T> result = new ArrayList<T>();
		for (T item : args)
			result.add(item);
		return result;
	}

	public static void main(String[] args) {
		List<String> ls = makeList("A");
		System.out.println(ls);
		ls = makeList("A", "B", "C");
		System.out.println(ls);
		ls = makeList("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split(""));
		System.out.println(ls);
	}
} /*
	 * Output: [A] [A, B, C] [, A, B, C, D, E, F, F, H, I, J, K, L, M, N, O, P, Q,
	 * R, S, T, U, V, W, X, Y, Z]
	 */// :~

7.Java泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。因此List和List在运行时事实上是相同的类型。这两种形式都被擦除成他们的“原生”类型,即List。
8.Here’s a C++ example which uses templates. You’ll notice that the syntax for parameterized
types is quite similar, because Java took inspiration from C++:

//: generics/Templates.cpp
#include<iostream>using namespace std;template<
class T>
class Manipulator {
		T obj;

public:
Manipulator(T x) { obj = x; }

		void manipulate() {
			obj.f();
		}
	};

	class HasF {
public:
void f() { cout << "HasF::f()" << endl; }
	};

int main() {
HasF hf;

Manipulator<HasF> manipulator(hf);
manipulator.manipulate();
}/* Output:
HasF::f()
///:~

The Manipulator class stores an object of type T. What’s interesting is the manipulate( )
method, which calls a method f( ) on obj. How can it know that the f( ) method exists for the
type parameter T? The C++ compiler checks when you instantiate the template, so at the
point of instantiation of Manipulator , it sees that HasF has a method f( ). If it
were not the case, you’d get a compile-time error, and thus type safety is preserved.
Writing this kind of code in C++ is straightforward because when a template is instantiated,
the template code knows the type of its template parameters. Java generics are different.
Here’s the translation of HasF:

//: generics/HasF.java
public class HasF {
	public void f() {
		System.out.println("HasF.f()");
	}
} /// :~

If we take the rest of the example and translate it to Java, it won’t compile:

//: generics/Manipulation.java
// {CompileTimeError} (Won’t compile)
class Manipulator<T> {
	private T obj;

	public Manipulator(T x) {
		obj = x;
	}

// Error: cannot find symbol: method f():
	public void manipulate() {
		obj.f();
	}
}

public class Manipulation {
	public static void main(String[] args) {
		HasF hf = new HasF();
		Manipulator<HasF> manipulator = new Manipulator<HasF>(hf);
		manipulator.manipulate();
	}
} /// :

~

Because of erasure, the Java compiler can’t map the requirement that manipulate( ) must
be able to call f( ) on obj to the fact that HasF has a method f( ). In order to call f( ), we
must assist the generic class by giving it a bound that tells the compiler to only accept types
that conform to that bound. This reuses the extends keyword. Because of the bound, the
following compiles:

//: generics/Manipulator2.java
class Manipulator2<T extends HasF> {
	private T obj;

	public Manipulator2(T x) {
		obj = x;
	}

	public void manipulate() {
		obj.f();
	}
} /// :~

9.擦除的核心动机是它使得泛化的客户端可以用非泛化的类库来使用,反之亦然,这经常被称为“迁移兼容性”。Java泛型不仅必须支持向后兼容性,即现有的代码和类文件仍旧合法,并且继续保持之前的含义;而且还要支持向后兼容性,使得类库按照它们自己的步调变为泛型的,并且当某个类库变为泛型时,不会破坏依赖它的代码和应用程序。在确定这就是目标之后,擦除就被认定为是唯一可行的解决方案。通过允许非泛型的代码和泛型代码共存,擦除使得这种想着泛型的迁移成为可能。
10.注意,对于在泛型中创建数组,使用Array.newInstance()是推荐的方式。 11.因为擦除在方法体中移除了类型信息,所以在运行时的问题就是边界:即对象进入和离开方法的地点。这些正是编译器在编译期执行类型检查并插入转型代码的地方。

//: generics/Erased.java
// {CompileTimeError} (Won’t compile)
public class Erased<T> {
	private final int SIZE = 100;

	public static void f(Object arg) {
		if (arg instanceof T) {
		} // Error
		T var = new T(); // Error
		T[] array = new T[SIZE]; // Error
		T[] array = (T) new Object[SIZE]; // Unchecked warning
	}
} /// :~

12.不能创建泛型数组。一般的解决方案是任何想要泛型数组的地方都使用ArrayList。


//: generics/ListOfGenerics.java
import java.util.*;

public class ListOfGenerics<T> {
	private List<T> array = new ArrayList<T>();

	public void add(T item) {
		array.add(item);
	}

	public T get(int index) {
		return array.get(index);
	}
} /// :~
//: generics/ArrayOfGeneric.java
public class ArrayOfGeneric {
	static final int SIZE = 100;
	static Generic<Integer>[] gia;

	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
// Compiles; produces ClassCastException:
//! gia = (Generic<Integer>[])new Object[SIZE];
// Runtime type is the raw (erased) type:
		gia = (Generic<Integer>[]) new Generic[SIZE];
		System.out.println(gia.getClass().getSimpleName());
		gia[0] = new Generic<Integer>();
//! gia[1] = new Object(); // Compile-time error
// Discovers type mismatch at compile time:
//! gia[2] = new Generic<Double>();
	}
} /*
	 * Output: Generic[]
	 */// :~

The problem is that arrays keep track of their actual type, and that type is established at the
point of creation of the array. So even though gia has been cast to a Generic < Integer >[],
that information only exists at compile time (and without the @SuppressWarnings
annotation, you’d get a warning for that cast). At run time, it’s still an array of Object, and that causes problems. The only way to successfully create an array of a generic type is to
create a new array of the erased type, and cast that.

//: generics/GenericArray.java
public class GenericArray<T> {
	private T[] array;

	@SuppressWarnings("unchecked")
	public GenericArray(int sz) {
		array = (T[]) new Object[sz];
	}

	public void put(int index, T item) {
		array[index] = item;
	}

	public T get(int index) {
		return array[index];
	}

// Method that exposes the underlying representation:
	public T[] rep() {
		return array;
	}

	public static void main(String[] args) {
		GenericArray<Integer> gai = new GenericArray<Integer>(10);
// This causes a ClassCastException:
//! Integer[] ia = gai.rep();
// This is OK:
		Object[] oa = gai.rep();
	}
} /// :~

As before, we can’t say T[] array = new T[sz], so we create an array of objects and cast it.
The rep( ) method returns a T[], which in main( ) should be an Integer[] for gai, but if
you call it and try to capture the result as an Integer [] reference, you get a
ClassCastException, again because the actual runtime type is Object[].
14.The real issue is that we are talking about the type of the container, rather than the type that
the container is holding. Unlike arrays, generics do not have built-in covariance. This is
because arrays are completely defined in the language and can thus have both compile-time
and runtime checks built in, but with generics, the compiler and runtime system cannot
know what you want to do with your types and what the rules should be.
Sometimes, however, you’d like to establish some kind of upcasting relationship between the
two. This is what wildcards allow.


//: generics/GenericsAndCovariance.java
import java.util.*;

public class GenericsAndCovariance {
	public static void main(String[] args) {
// Wildcards allow covariance:
		List<? extends Fruit> flist = new ArrayList<Apple>();
// Compile Error: can’t add any type of object:
// flist.add(new Apple());
// flist.add(new Fruit());
// flist.add(new Object());
		flist.add(null); // Legal but uninteresting
// We know that it returns at least Fruit:
		Fruit f = flist.get(0);
	}
} /// :~
第十六章 数组

1.数组是一种效率最高的存储和随机访问对象引用序列的方式。数组就是一个简单的线性序列,这使得元素访问非常迅速。但代价是数组对象的大小被固定,并且在其生命周期内不可改变。
2.“[]”是访问数组对象唯一的方式。
3.对象数组和基本类型的数组在使用上几乎是相同的;唯一的区别就是对象数组保存的是引用,基本类型的数组直接保存基本类型的值。
4.数组的length表示数组的容量,而不是数组实际存储了元素的个数。
5.新生成一个数组对象时,其中所有的引用都被自动初始化为null;所以检查其中的引用是否为null,就可以知道数组中的某个位置是否存有对象。同样,基本类型也会被初始化。
6.对于基本类型的数组,可以通过使用花括号将每个分量分隔开。


//: arrays/MultidimensionalPrimitiveArray.java
// Creating multidimensional arrays.
import java.util.*;

public class MultidimensionalPrimitiveArray {
	public static void main(String[] args) {
		int[][] a = { { 1, 2, 3, }, { 4, 5, 6, }, };
		System.out.println(Arrays.deepToString(a));
	}
} /*
	 * Output: [[1, 2, 3], [4, 5, 6]]
	 */// :~

You can also allocate an array using new. Here’s a three-dimensional array allocated in a
new expression:


//: arrays/ThreeDWithNew.java
import java.util.*;

public class ThreeDWithNew {
	public static void main(String[] args) {
// 3-D array with fixed length:
		int[][][] a = new int[2][2][4];
		System.out.println(Arrays.deepToString(a));
	}
} /*
	 * Output: [[[0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0]]]
	 */// :~

Each vector in the arrays that make up the matrix can be of any length (this is called a ragged
array):


//: arrays/RaggedArray.java
import java.util.*;

public class RaggedArray {
	public static void main(String[] args) {
		Random rand = new Random(47);
// 3-D array with varied-length vectors:
		int[][][] a = new int[rand.nextInt(7)][][];
		for (int i = 0; i < a.length; i++) {
			a[i] = new int[rand.nextInt(5)][];
			for (int j = 0; j < a[i].length; j++)
				a[i][j] = new int[rand.nextInt(5)];
		}
		System.out.println(Arrays.deepToString(a));
	}
} /*
	 * Output: [[], [[0], [0], [0, 0, 0, 0]], [[], [0, 0], [0, 0]], [[0, 0, 0], [0],
	 * [0, 0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0], []], [[0], [], [0]]]
	 */// :~
	 

The first new creates an array with a random-length first element and the rest
undetermined. The second new inside the for loop fills out the elements but leaves the third
index undetermined until you hit the third new.

Autoboxing also works with array initializers:


//: arrays/AutoboxingArrays.java
import java.util.*;

public class AutoboxingArrays {
	public static void main(String[] args) {
		Integer[][] a = { // Autoboxing:
				{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, { 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 },
				{ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 }, { 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 }, };
		System.out.println(Arrays.deepToString(a));
	}
} /*
	 * Output: [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [21, 22, 23, 24, 25, 26, 27, 28,
	 * 29, 30], [51, 52, 53, 54, 55, 56, 57, 58, 59, 60], [71, 72, 73, 74, 75, 76,
	 * 77, 78, 79, 80]]
	 */// :~

7.数组和泛型不能很好的结合 ,你不能实例化具有参数化的数组。

Peel<Banana>[] peels = new Peel<Banana> [10]; // Illegal

擦除会移除参数类型信息,而数组必须知道他们所持有的确切类型,以强制保证类型安全。
但是,你可以参数化数组本身的类型。

//: arrays/ParameterizedArrayType.java
class ClassParameter<T> {
	public T[] f(T[] arg) {
		return arg;
	}
}

class MethodParameter {
	public static <T> T[] f(T[] arg) {
		return arg;
	}
}

public class ParameterizedArrayType {
	public static void main(String[] args) {
		Integer[] ints = { 1, 2, 3, 4, 5 };
		Double[] doubles = { 1.1, 2.2, 3.3, 4.4, 5.5 };
		Integer[] ints2 = new ClassParameter<Integer>().f(ints);
		Double[] doubles2 = new ClassParameter<Double>().f(doubles);
		ints2 = MethodParameter.f(ints);
		doubles2 = MethodParameter.f(doubles);
	}
} /// :~

正如上例所证实的那样,不能创建泛型数组的说法并不十分准确,诚然,编译器确实不会让你实例化泛型数组,但是,它允许你创建对这种数组的引用。

List<String>[] ls;

尽管你不能创建实际的持有泛型的数组对象,但是你可以创建非泛型的数组,然后将其转型。


//: arrays/ArrayOfGenerics.java
// It is possible to create arrays of generics.
import java.util.*;

public class ArrayOfGenerics {
	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		List<String>[] ls;
		List[] la = new List[10];
		ls = (List<String>[]) la; // "Unchecked" warning
		ls[0] = new ArrayList<String>();
// Compile-time checking produces an error:
//! ls[1] = new ArrayList<Integer>();
// The problem: List<String> is a subtype of Object
		Object[] objects = ls; // So assignment is OK
// Compiles and runs without complaint:
		objects[1] = new ArrayList<Integer>();
// However, if your needs are straightforward it is
// possible to create an array of generics, albeit
// with an "unchecked" warning:
		List<BerylliumSphere>[] spheres = (List<BerylliumSphere>[]) new List[10];
		for (int i = 0; i < spheres.length; i++)
			spheres[i] = new ArrayList<BerylliumSphere>();
	}
} /// :~

Once you have a reference to a List[], you can see that you get some compile-time
checking. The problem is that arrays are covariant, so a List[] is also an Object[],
and you can use this to assign an ArrayList into your array, with no error at
either compile time or run time.
If you know you’re not going to upcast and your needs are relatively simple, however, it is
possible to create an array of generics, which will provide basic compile-time type checking.
However, a generic container will virtually always be a better choice than an array of
generics.
In general you’ll find that generics are effective at the boundaries of a class or method. In the
interiors, erasure usually makes generics unusable. So you cannot, for example, create an
array of a generic type:

//: arrays/ArrayOfGenericType.java
// Arrays of generic types won’t compile.
public class ArrayOfGenericType<T> {
	T[] array; // OK

	@SuppressWarnings("unchecked")
	public ArrayOfGenericType(int size) {
//! array = new T[size]; // Illegal
		array = (T[]) new Object[size]; // "unchecked" Warning
	}
// Illegal:
//! public <U> U[] makeArray() { return new U[10]; }
} /// :~

Erasure gets in the way again—this example attempts to create arrays of types that have been
erased, and are thus unknown types. Notice that you can create an array of Object, and cast
it, but without the @SuppressWarnings annotation you get an “unchecked” warning at
compile time because the array doesn’t really hold or dynamically check for type T. That is, if
I create a String[], Java will enforce at both compile time and run time that I can only place
String objects in that array. However, if I create an Object[], I can put anything into that
array except primitive types.
8.The Java standard library provides a static method, System.arraycopy( ), which can copy
arrays far more quickly than if you use a for loop to perform the copy by hand.System.arraycopy( ) will not perform autoboxing or autounboxing—the two arrays must
be of exactly the same type.
9.浅拷贝
The example shows that both primitive arrays and object arrays can be copied. However, if
you copy arrays of objects, then only the references get copied—there’s no duplication of the
objects themselves. This is called a shallow copy (see the online supplements for this book
for more details).


//: arrays/CopyingArrays.java
// Using System.arraycopy()
import java.util.*;
import static net.mindview.util.Print.*;

public class CopyingArrays {
	public static void main(String[] args) {
		int[] i = new int[7];
		int[] j = new int[10];
		Arrays.fill(i, 47);
		Arrays.fill(j, 99);
		print("i = " + Arrays.toString(i));
		print("j = " + Arrays.toString(j));
		System.arraycopy(i, 0, j, 0, i.length);
		print("j = " + Arrays.toString(j));
		int[] k = new int[5];
		Arrays.fill(k, 103);
		System.arraycopy(i, 0, k, 0, k.length);
		print("k = " + Arrays.toString(k));
		Arrays.fill(k, 103);
		System.arraycopy(k, 0, i, 0, k.length);
		print("i = " + Arrays.toString(i));
// Objects:
		Integer[] u = new Integer[10];
		Integer[] v = new Integer[5];
		Arrays.fill(u, new Integer(47));
		Arrays.fill(v, new Integer(99));
		print("u = " + Arrays.toString(u));
		print("v = " + Arrays.toString(v));
		System.arraycopy(v, 0, u, u.length / 2, v.length);
		print("u = " + Arrays.toString(u));
	}
} /*
	 * Output: i = [47, 47, 47, 47, 47, 47, 47] j = [99, 99, 99, 99, 99, 99, 99, 99,
	 * 99, 99] j = [47, 47, 47, 47, 47, 47, 47, 99, 99, 99] k = [47, 47, 47, 47, 47]
	 * i = [103, 103, 103, 103, 103, 47, 47] u = [47, 47, 47, 47, 47, 47, 47, 47,
	 * 47, 47] v = [99, 99, 99, 99, 99] u = [47, 47, 47, 47, 47, 99, 99, 99, 99, 99]
	 */// :~

10.Arrays类提供了重载后的equals方法,用来比较整个数组,针对于所有的基本类型和Object都进行了重载。数组相同的条件是:元素个数相同,并且对应位置上的元素也相等。对于基本类型,需要使用基本类型的包装器类型的equals方法。
11.Java有两种方式来提供比较功能。The first is with the “natural”
comparison method that is imparted to a class by implementing the
java.lang.Comparable interface. This is a very simple interface with a single method,
compareTo( ). This method takes another object of the same type as an argument and
produces a negative value if the current object is less than the argument, zero if the argument is
equal, and a positive value if the current object is greater than the argument.


//: arrays/CompType.java
// Implementing Comparable in a class.
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

public class CompType implements Comparable<CompType> {
	int i;
	int j;
	private static int count = 1;

	public CompType(int n1, int n2) {
		i = n1;
		j = n2;
	}

	public String toString() {
		String result = "[i = " + i + ", j = " + j + "]";
		if (count++ % 3 == 0)
			result += "\n";
		return result;
	}

	public int compareTo(CompType rv) {
		return (i < rv.i ? -1 : (i == rv.i ? 0 : 1));
	}

	private static Random r = new Random(47);

	public static Generator<CompType> generator() {
		return new Generator<CompType>() {
			public CompType next() {
				return new CompType(r.nextInt(100), r.nextInt(100));
			}
		};
	}

	public static void main(String[] args) {
		CompType[] a = Generated.array(new CompType[12], generator());
		print("before sorting:");
		print(Arrays.toString(a));
		Arrays.sort(a);
		print("after sorting:");
		print(Arrays.toString(a));
	}
} /*
	 * Output: before sorting: [[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
	 * , [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28] , [i = 51, j = 89], [i =
	 * 9, j = 78], [i = 98, j = 61] , [i = 20, j = 58], [i = 16, j = 40], [i = 11, j
	 * = 22] ] after sorting: [[i = 9, j = 78], [i = 11, j = 22], [i = 16, j = 40] ,
	 * [i = 20, j = 58], [i = 22, j = 7], [i = 51, j = 89] , [i = 58, j = 55], [i =
	 * 61, j = 29], [i = 68, j = 0] , [i = 88, j = 28], [i = 93, j = 61], [i = 98, j
	 * = 61] ]
	 */// :~

The Collections class (which we’ll look at more in the next chapter) contains a method
reverseOrder( ) that produces a Comparator to reverse the natural sorting order. This can be
applied to CompType:


//: arrays/Reverse.java
// The Collections.reverseOrder() Comparator
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

public class Reverse {
	public static void main(String[] args) {
		CompType[] a = Generated.array(new CompType[12], CompType.generator());
		print("before sorting:");
		print(Arrays.toString(a));
		Arrays.sort(a, Collections.reverseOrder());
		print("after sorting:");
		print(Arrays.toString(a));
	}
} /*
	 * Output: before sorting: [[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
	 * , [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28] , [i = 51, j = 89], [i =
	 * 9, j = 78], [i = 98, j = 61] , [i = 20, j = 58], [i = 16, j = 40], [i = 11, j
	 * = 22] ] after sorting: [[i = 98, j = 61], [i = 93, j = 61], [i = 88, j = 28]
	 * , [i = 68, j = 0], [i = 61, j = 29], [i = 58, j = 55] , [i = 51, j = 89], [i
	 * = 22, j = 7], [i = 20, j = 58] , [i = 16, j = 40], [i = 11, j = 22], [i = 9,
	 * j = 78] ]
	 */// :~

You can also write your own Comparator. This one compares CompType objects based on
their j values rather than their i values:


//: arrays/ComparatorTest.java
// Implementing a Comparator for a class.
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

class CompTypeComparator implements Comparator<CompType> {
	public int compare(CompType o1, CompType o2) {
		return (o1.j < o2.j ? -1 : (o1.j == o2.j ? 0 : 1));
	}
}

public class ComparatorTest {
	public static void main(String[] args) {
		CompType[] a = Generated.array(new CompType[12], CompType.generator());
		print("before sorting:");
		print(Arrays.toString(a));
		Arrays.sort(a, new CompTypeComparator());
		print("after sorting:");
		print(Arrays.toString(a));
	}
} /*
	 * Output: before sorting: [[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29]
	 * , [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28] , [i = 51, j = 89], [i =
	 * 9, j = 78], [i = 98, j = 61] , [i = 20, j = 58], [i = 16, j = 40], [i = 11, j
	 * = 22] ] after sorting: [[i = 68, j = 0], [i = 22, j = 7], [i = 11, j = 22] ,
	 * [i = 88, j = 28], [i = 61, j = 29], [i = 16, j = 40] , [i = 58, j = 55], [i =
	 * 20, j = 58], [i = 93, j = 61] , [i = 98, j = 61], [i = 9, j = 78], [i = 51, j
	 * = 89] ]
	 */// :~

12.One thing you’ll notice about the output in the String sorting algorithm is that it’s
lexicographic, so it puts all the words starting with uppercase letters first, followed by all the
words starting with lowercase letters. (Telephone books are typically sorted this way.) If you
want to group the words together regardless of case, use
String.CASE_INSENSITIVE_ORDER as shown in the last call to sort( ) in the above
example.
The sorting algorithm that’s used in the Java standard library is designed to be optimal for
the particular type you’re sorting—a Quicksort for primitives, and a stable merge sort for
objects. You don’t need to worry about performance unless your profiler points you to the
sorting process as a bottleneck.
13.Once an array is sorted, you can perform a fast search for a particular item by using
Arrays.binarySearch( ). However, if you try to use binarySearchC ) on an unsorted
array the results will be unpredictable.If an array contains duplicate elements, there is no guarantee which of those duplicates will be found. The search algorithm is not designed to support duplicate elements, but rather to tolerate them. If you need a sorted list of non-duplicated elements, use a TreeSet (to maintain sorted order) or LinkedHashSet (to maintain insertion order). These classes take care of all the details for you automatically. Only in cases of performance bottlenecks should you replace one of these classes with a hand-maintained array.
14.If you sort an object array using a Comparator (primitive arrays do not allow sorting with a
Comparator), you must include that same Comparator when you perform a
binarySearch( ) (using the overloaded version of binarySearch( )). For example, the
StringSorting.java program can be modified to perform a search:


//: arrays/AlphabeticSearch.java
// Searching with a Comparator.
import java.util.*;
import net.mindview.util.*;

public class AlphabeticSearch {
	public static void main(String[] args) {
		String[] sa = Generated.array(new String[30], new RandomGenerator.String(5));
		Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER);
		System.out.println(Arrays.toString(sa));
		int index = Arrays.binarySearch(sa, sa[10], String.CASE_INSENSITIVE_ORDER);
		System.out.println("Index: " + index + "\n" + sa[index]);
	}
} /*
	 * Output: [bkIna, cQrGs, cXZJo, dLsmw, eGZMm, EqUCB, gwsqP, hKcxr, HLGEa,
	 * HqXum, HxxHv, JMRoE, JmzMs, Mesbt, MNvqe, nyGcF, ogoYW, OneOE, OWZnT, RFJQA,
	 * rUkZP, sgqia, slJrL, suEcU, uTpnX, vpfFv, WHkjU, xxEAJ, YNzbr, zDyCy] Index:
	 * 10 HxxHv
	 */// :~
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值