第13章 字符串
1 String对象是不可变,String类中每一个修改String值的方法实际都是创建一个全新的String对象
2 " + "与" += "是Java中仅有的两个重载过的操作符,而Java并不允许重载任何操作符。
3 mac下用Java命令
package c06;
public class ArrayApp {
public static void main(String[] args){
String mango = "mango";
String s = "abc "+mango+" def "+47;
System.out.println(s);
}
}
$ cd src // 要到src目录下
$ javac c06/ArrayApp.java // c06为包目录名,ArrayApp.java为文件名
$ java c06.ArrayApp // 执行ArrayApp.java
abc mango def 47 // 执行结果
$ javap -c c06.ArrayApp // 反编译
Compiled from "ArrayApp.java"
public class c06.ArrayApp {
public c06.ArrayApp();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String mango
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: ldc #5 // String abc
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #7 // String def
21: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: bipush 47
26: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
29: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
32: astore_2
33: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
36: aload_2
37: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: return
}
4 StringBuilder,包含insert(),replace(),substring(),reverse(),append(),toString()和delete()
public class ArrayApp {
public static Random rand = new Random(47);
public String toString(){
StringBuilder result = new StringBuilder("[");
for(int i=0;i<25;i++){
result.append(rand.nextInt(100));
result.append(", ");
}
result.delete(result.length()-2, result.length());
result.append("]");
return result.toString();
}
public static void main(String[] args){
ArrayApp a = new ArrayApp();
System.out.println(a);
}
}
5 如果要打印对象的内存地址,可以使用this关键字。但如下所示,编译器看到String对象后面跟个"+",会试着把this转换成一个String,然后调用this.toString()就进入了循环的递归。所以此处得用super.toString()代替this
public class ArrayApp {
public static Random rand = new Random(47);
public String toString(){
//return "address: "+this+"\n";
return "address: "+super.toString()+"\n";
}
public static void main(String[] args){
List<ArrayApp> t = new ArrayList<ArrayApp>();
for(int i=0;i<10;i++)
t.add(new ArrayApp());
System.out.println(t);
}
}
6 String的操作
方法 | 参数,重载版本 | 应用 |
构造器 | 重载版本:默认版本,String,StringBuilder,StringBuffer,char数组,byte数组 | 创建String对象 |
length() | String中字符的个数 | |
charAt() | 参数:int索引 | 取得String中该索引位置上的char |
getChars(),getBytes() | 参数:要复制部分的起点和终点的索引,复制的目标数组,目标数组的起始索引 | 复制char或byte到一个目标数组中 |
toCharArray() | 生成一个char[ ],包含String的所有字符 | |
equals() | 参数:与之比较的String | 比较两个String内容是否相同,相同则返回true |
compareTo() | 参数:与之比较的String | 按词典顺序比较String内容大小,结果为负数,零或正数 |
contains() | 参数:要搜索的CharSequence | String对象包含参数内容则返回true |
contentEquals() | 参数:与之比较的CharSequence或StringBuffer | String对象与参数内容完全一致则返回true |
equalsIgnoreCase() | 参数:与之比较的String | 忽略大小写,比较两个String内容是否相同,相同则返回true |
regionMatcher() | 参数:该String的索引偏移量,另一个String及其索引偏移量,要比较长度 | 返回boolean结果,以表明所比较区域是否相等 |
startsWith() | 参数:起始String | 返回boolean结果,以表明该String是否以此参数起始 |
endsWith() | 参数:后缀String | 返回boolean结果,以表明该String是否以此参数后缀 |
indexOf(),lastIndexOf() | 重载版本:char,char与起始索引,String,String与起始索引 | 如果不包含参数则返回-1,否则返回该参数在String中的起始索引。lastIndexOf()是从后往前搜索 |
substring() | 重载版本:起始索引,起始索引和终点坐标 | 返回一个新的子String,包含参数指定区域的字符串 |
concat() | 参数:要连接的String | 返回新的String,内容为原始String连接参数String |
replace() | 参数:要替换掉的字符 | 返回替换后的新String |
toLowerCase(),toUpperCase() | String全部变成小写/大写字符 | |
trim() | 去掉String两端空白字符,返回新String | |
valueOf() | 重载版本:Object;char[];char[]和偏移量和字符个数;boolean;char;int;float;long;double | 返回表示参数内容的String,即将其他类型转换为String类型 |
intern() | 为每个唯一的字符序列生成一个且仅生成一个String引用 |
7 格式化输出,如下所示三者输出结果一样
int x = 5;
double y = 5.332542;
System.out.println("Row 1: ["+x+" "+y+"]");
System.out.printf("Row 1: [%d %f]\n",x,y);
System.out.format("Row 1: [%d %f]\n",x,y);
8 在Java中,所有新的格式化功能都由java.util.Formatter类处理。可以将Formatter看作一个翻译器,将格式化字符串与数据翻译成需要的结果。Formatter类将内容翻译为字符串,由PrintStream进行输出
public class ArrayApp {
private String name;
private Formatter f;
public ArrayApp(String name,Formatter f){
this.name = name;
this.f = f;
}
public void move(int x,int y){
f.format("%s The ArrayApp is at (%d,%d)\n", name,x,y);
}
public static void main(String[] args){
PrintStream outAlias = System.out;
ArrayApp tommy = new ArrayApp("Tommy",new Formatter(System.out));
ArrayApp terry = new ArrayApp("Terry",new Formatter(outAlias));
tommy.move(0, 0);
terry.move(4, 3);
tommy.move(3, 4);
terry.move(2, 5);
tommy.move(3, 3);
terry.move(3, 3);
}
}
9 格式化说明符,语法如下
%[argument_index$][flags][width][.precision]conversion // precision前面有个小数点
注:
- width控制一个域的最小尺寸。默认情况是右对齐,但可以通过使用“-“标志来改变对齐方向。width可以应用于各种数据类型
- 不是所有类型都能使用precision,而且不同类型使用情况也不一样。将precision用于String时表示打印String时输出字符的最大数量,用于float表示小数点后位数(默认6位小数)。precision不能用于整数,会触发异常
public class ArrayApp {
private double total = 0;
private Formatter f = new Formatter(System.out);
public void printTitle(){
f.format("%-15s %5s %10s\n","Item", "Qty","Price");
f.format("%-15s %5s %10s\n", "----","--","-----");
}
public void print(String name,int qty,double price){
f.format("%-15.15s %5d %10.2f\n", name,qty,price);
total += price;
}
public void printTotal(){
f.format("%-15s %5s %10.2f\n", "Tax","",total*0.06);
f.format("%-15s %5s %10s\n", "","","-----");
f.format("%-15s %5s %10.2f\n", "Total","",total*1.06);
}
public static void main(String[] args){
ArrayApp a = new ArrayApp();
a.printTitle();
a.print("Jack's Magic Beans",4,4.25);
a.print("Princess Peas", 3, 5.1);
a.print("Three Bears Porridge", 1, 14.29);
a.printTotal();
}
}
输出结果
Item Qty Price
---- -- -----
Jack's Magic Be 4 4.25
Princess Peas 3 5.10
Three Bears Por 1 14.29
Tax 1.42
-----
Total 25.06
10 Formatter类型转换字符
d | 整数型(十进制) |
c | Unicode字符 |
b | Boolean值 |
s | String |
f | 浮点数(十进制) |
e | 浮点数(科学计数) |
x | 整数(十六进制) |
h | 散列码(十六进制) |
% | 字符"%" |
Formatter f = new Formatter(System.out);
char u = 'a';
System.out.println("u = 'a'");
f.format("s: %s\n", u); // ok, s: a
//f.format("d: %d\n", u); //error, d != java.lang.Character
f.format("c: %c\n", u); // ok, c: a
f.format("b: %b\n", u); // ok, b: true
//f.format("f: %f\n", u); // error, f != java.lang.Character
//f.format("e: %e\n", u); // error, e != java.lang.Character
//f.format("x: %x\n", u); // error, x != java.lang.Character
f.format("h: %h\n", u); // ok, h: 61
int v = 121;
System.out.println("v = 121");
f.format("s: %s\n", v); // ok, s: 121
f.format("d: %d\n", v); // ok, d: 121
f.format("c: %c\n", v); // ok, c: y
f.format("b: %b\n", v); // ok, b: true
//f.format("f: %f\n", v); // error, f != java.lang.Integer
//f.format("e: %e\n", v); // error, e != java.lang.Integer
f.format("x: %x\n", v); // ok, x: 79
f.format("h: %h\n", v); // ok, h: 79
BigInteger w = new BigInteger("500000000000000000000");
System.out.println("w = 500000000000000000000");
f.format("s: %s\n", w); // ok, s: 500000000000000000000
f.format("d: %d\n", w); // ok, d: 500000000000000000000
//f.format("c: %c\n", w); // error, c != java.math.BigInteger
f.format("b: %b\n", w); // ok, b: true
//f.format("f: %f\n", w); // error, f != java.math.BigInteger
//f.format("e: %e\n", w); // error, e != java.math.BigInteger
f.format("x: %x\n", w); // ok, x: 1b1ae4d6e2ef500000
f.format("h: %h\n", w); // ok, h: 31066ab9
double x = 179.543;
System.out.println("x = 179.543");
f.format("s: %s\n", x); // ok, s: 179.543
//f.format("d: %d\n", x); // error, d != java.lang.Double
//f.format("c: %c\n", x); // error, c != java.lang.Double
f.format("b: %b\n", x); // ok, b: true
f.format("f: %f\n", x); // ok, f: 179.543000
f.format("e: %e\n", x); // ok, e: 1.795430e+02
//f.format("x: %x\n", x); // error, x != java.lang.Double
f.format("h: %h\n", x); // ok, h: 1ef462c
ArrayApp y = new ArrayApp();
System.out.println("y = new ArrayApp()");
f.format("s: %s\n", y); // ok, s: c06.ArrayApp@146bf551
//f.format("d: %d\n", y); // error, d != c06.ArrayApp
//f.format("c: %c\n", y); // error, c != c06.ArrayApp
f.format("b: %b\n", y); // ok, b: true
//f.format("f: %f\n", y); // error, f != c06.ArrayApp
//f.format("e: %e\n", y); // error, e != c06.ArrayApp
//f.format("x: %x\n", y); // error, x != c06.ArrayApp
f.format("h: %h\n", y); // ok, h: 146bf551
boolean z = false;
System.out.println("z = false");
f.format("s: %s\n", z); // ok, s: false
//f.format("d: %d\n", z); // error, d != java.lang.Boolean
//f.format("c: %c\n", z); // error, c != java.lang.Boolean
f.format("b: %b\n", z); // ok, b: false
//f.format("f: %f\n", z); // error, f != java.lang.Boolean
//f.format("e: %e\n", z); // error, e != java.lang.Boolean
//f.format("x: %x\n", z); // error, x != java.lang.Boolean
f.format("h: %h\n", z); // ok, h: 4d5
11 对于转换字符b,只要参数不为null,转换结果永远都是true,即使数字0也是true。
12 String.format(),功能跟Formatter类似,但方便不用创建类
public class ArrayApp extends Exception {
public ArrayApp(int transactionID,int queryID,String message){
super(String.format("(t%d,q%d) %s",transactionID,queryID,message));
}
public static void main(String[] args){
try{
throw new ArrayApp(3,7,"Write failed");
}catch(Exception e){
System.out.println(e);
}
}
}
13 “%010d” 表示输出宽度为10位的整数,如果变量不够10位,左侧添加0来补充位数
14 在Java中,“\\d”表示一位数字。“-?\\d+”表示可能有一个负号,后面跟着一位或多位数字。“(-|\\+)?”表示可能有一个-或+,或者二者都没有
System.out.println("-1234".matches("-?\\d+")); // true
System.out.println("5678".matches("-?\\d+")); // true
System.out.println("+911".matches("-?\\d+")); // false
System.out.println("+911".matches("(-|\\+)?\\d+")); // true
15 "\\W"表示非单词字符,“\\w”表示一个单词字符,“n\\W+”表示字母n后面跟着一个或多个非单词字符。
public class ArrayApp {
public static String knights = "Then, when you have found the shrubbery, you must "
+"cut down the mightiest tree in the forest... "
+ "with... a herring!";
public static void split(String regex){
System.out.println(Arrays.toString(knights.split(regex)));
}
public static void main(String[] args){
split(" ");
split("\\W+");
split("n\\W+");
}
}
输出
[Then,, when, you, have, found, the, shrubbery,, you, must, cut, down, the, mightiest, tree, in, the, forest..., with..., a, herring!]
[Then, when, you, have, found, the, shrubbery, you, must, cut, down, the, mightiest, tree, in, the, forest, with, a, herring]
[The, whe, you have found the shrubbery, you must cut dow, the mightiest tree i, the forest... with... a herring!]
16 String类自带一个正则表达式的替换工具
public class ArrayApp {
public static String knights = "Then, when you have found the shrubbery, you must "
+"cut down the mightiest tree in the forest... "
+ "with... a herring!";
public static void main(String[] args){
System.out.println(knights.replaceFirst("f\\w+", "located"));
System.out.println(knights.replaceAll("shrubbery|tree|herring", "banana"));
}
}
输出
Then, when you have located the shrubbery, you must cut down the mightiest tree in the forest... with... a herring!
Then, when you have found the banana, you must cut down the mightiest banana in the forest... with... a banana!
17 字符类
点号( . ) | 任意字符 |
[abc] | a|b|c,包含a,b和c三者中任意字符 |
[^abc] | 除了a,b和c之外的任意字符 |
[a-zA-Z] | 从a到z或从A到Z的任意字符 |
[abc[hij]] | a|b|c|h|i|j,任意a,b,c,h,i和j字符(并集) |
[a-z&&[hij]] | 任意h,i或j (交集) |
\s | 空白字符 |
\S | 非空白字符 |
\d | 数字[0-9] |
\D | 非数字[^0-9] |
\w | 单词字符[a-zA-Z0-9] |
\W | 非单词字符 |
18 正则逻辑操作符
XY | Y跟在X后面 |
X|Y | X或Y |
(X) | 捕获组 |
19 边界匹配符
^ | 一行的开始 |
$ | 一行的结束 |
\b | 词的边界 |
\B | 非词的边界 |
\G | 前一个匹配的结束 |
// 输出都是true
for(String pattern:new String[]{"Rudolph","[rR]udolph","[rR][aeiou][a-z]ol.*","R.*"})
System.out.println("Rudolph".matches(pattern));
20 量词(X必须要用圆括号括起来)
贪婪型 | 勉强型 | 占有型 | 如何匹配 |
X? | X?? | X?+ | 一个或零个X |
X* | X*? | X*+ | 零个或多个X |
X+ | X+? | X++ | 一个或多个X |
X{n} | X{n}? | X{n}+ | 恰好n次X |
X{n,} | X{n,}? | X{n,}+ | 至少n次X |
X{n,m} | X{n,m}? | X{n,m}+ | 至少n,且不超过m次X |
21 Matcher.find()在CharSequence中查找多个匹配。第二个find()能够接收一个整数作为参数,该整数表示字符串中字符的位置,并以其作为搜索的起点。
Matcher m = Pattern.compile("\\w+")
.matcher("Evening is full of the linnet's wings");
while(m.find())
System.out.print(m.group()+" ");
System.out.println();
int i = 0;
while(m.find(i)){
System.out.print(m.group()+" ");
i++;
}
Evening is full of the linnet s wings
Evening vening ening ning ing ng g is is s full full ull ll l of of f the the he e linnet linnet innet nnet net et t s s wings wings ings ngs gs s
22 扫描输入,readLine()读取一行
public static BufferedReader input = new BufferedReader(
new StringReader("Sir Robin of Camelot\n22 1.61803"));
public static void main(String[] args){
try{
System.out.println("What is your name?");
String name = input.readLine();
System.out.println(name);
System.out.println("How old are you? What is your favorite double?");
System.out.println("(input: <age> <double>)");
String numbers = input.readLine();
System.out.println(numbers);
String[] numArray = numbers.split(" ");
int age = Integer.parseInt(numArray[0]);
double favorite = Double.parseDouble(numArray[1]);
System.out.format("Hi %s.\n",name);
System.out.format("In 5 years you will be %d.\n",age+5);
System.out.format("My favorite double is %f", favorite/2);
}catch(IOException e){
e.printStackTrace();
}
}
输出
What is your name?
Sir Robin of Camelot
How old are you? What is your favorite double?
(input: <age> <double>)
22 1.61803
Hi Sir Robin of Camelot.
In 5 years you will be 27.
My favorite double is 0.809015
23 Scanner类,Scanner根据空白字符对输入进行分词。
Scanner stdin = new Scanner(System.in);
System.out.println("What is your name?");
String name = stdin.nextLine();
System.out.println(name);
System.out.println("How old are you? What is your favorite double?");
System.out.println("(input: <age> <double>)");
String numbers = stdin.nextLine();
System.out.println(numbers);
int age = stdin.nextInt();
double favorite = stdin.nextDouble();
System.out.format("Hi %s.\n",name);
System.out.format("In 5 years you will be %d.\n",age+5);
System.out.format("My favorite double is %f", favorite/2);
第14章 类型信息
1 传递this参数给System.out.println(),间接地使用toString()。把Shape子类对象放入List<Shape>的数组时会向上转型,会丢失Shape子类对象的具体类型。即对于Shape数组而言,他们只是Shape类对象。动态调用子类的draw()
abstract class Shape {
void draw() { System.out.println(this+".draw()");} // this即调用自身的toString()函数
abstract public String toString();
}
class Circle extends Shape{
public String toString(){ return "Circle";}
}
class Square extends Shape{
public String toString(){ return "Square";}
}
class Triangle extends Shape {
public String toString(){ return "Triangle"; }
}
public class ArrayApp {
public static void main(String[] args){
List<Shape>shapeList = Arrays.asList(new Circle(),new Square(),new Triangle());
for(Shape s:shapeList)
s.draw();
}
}
输出
Circle.draw()
Square.draw()
Triangle.draw()
2 每个类都有一个Class对象,即每当编写并且编译了一个新类,就会产生一个Class对象(更恰当地说,是被保存在一个同名的.class文件中)。为了生成这个类对象,运行这个程序的Java虚拟机(JVM)将使用被称为“类加载器”的子系统。
3 所有的类都是在对其第一次使用时,动态加载到JVM中的。当程序创建第一个对类的静态成员的引用时就会加载这个类。这个证明构造器也是类的静态方法,即使在构造器之前并没有使用static关键字。
4 类加载器首先检查这个类的Class对象是否已经加载。如果尚未加载,默认的类加载就会根据类名查找.class文件。一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。
package c06;
class Candy{
static {
System.out.println("Loading Candy");
}
}
class NewGum {
static {
System.out.println("Loading Gum");
}
}
class Cookie2{
static {
System.out.println("Loading Cookie");
}
}
public class ArrayApp {
public static void main(String[] args){
System.out.println("inside main");
new Candy();
System.out.println("After creating Candy");
//new NewGum();
try{
Class.forName("c06.NewGum"); // 类的全名,即包名.类名
}catch(ClassNotFoundException e){
System.out.println("Could not find Gum");
}
System.out.println("After Class.forName(\"Gum\")");
new Cookie2();
System.out.println("After creating Cookie");
}
}
输出
inside main
Loading Candy
After creating Candy
Loading Gum
After Class.forName("Gum")
Loading Cookie
After creating Cookie
Class.forName("类的全名(包名.类名)"),返回目标类的一个Class对象引用。
5 Class的部分函数
package c06;
interface HasBatteries {}
interface Waterproof{}
interface Shoots {}
class Toy{
Toy() {}
Toy(int i) {}
}
class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{
FancyToy(){ super(1); }
}
public class ArrayApp {
static void printInfo(Class cc){
System.out.println("Class name: "+cc.getName()+" is interface? ["
+cc.isInterface()+"]");
System.out.println("Simple name: "+cc.getSimpleName());
System.out.println("Canonical name: "+cc.getCanonicalName());
}
public static void main(String[] args){
Class c = null;
try{
c = Class.forName("c06.FancyToy");
}catch(ClassNotFoundException e){
System.out.println("Can't find FancyToy");
System.exit(1);
}
printInfo(c);
for(Class face:c.getInterfaces()){
printInfo(face);
}
Class up = c.getSuperclass();
Object obj = null;
try{
obj = up.newInstance();
}catch(InstantiationException e){
System.out.println("Cannot instantiate");
System.exit(1);
}catch(IllegalAccessException e){
System.out.println("Cannot access");
System.exit(1);
}
printInfo(obj.getClass());
}
}
输出
Class name: c06.FancyToy is interface? [false]
Simple name: FancyToy
Canonical name: c06.FancyToy
Class name: c06.HasBatteries is interface? [true]
Simple name: HasBatteries
Canonical name: c06.HasBatteries
Class name: c06.Waterproof is interface? [true]
Simple name: Waterproof
Canonical name: c06.Waterproof
Class name: c06.Shoots is interface? [true]
Simple name: Shoots
Canonical name: c06.Shoots
Class name: c06.Toy is interface? [false]
Simple name: Toy
Canonical name: c06.Toy
- forName():创建一个Class引用。在传递给forName()的字符串中,必须使用全限定名(包含包名)
- getName():返回全限定的类名
- getSimpleName():返回不包含包名的类名
- getCanonicalName():返回全限定类名
- isInterface():判断Class对象是否为接口
- getInterface():返回Class对象里的接口Class对象
- getSuperclass():返回Class对象的基类Class对象
- getClass():获取对象的Class引用
newInstance()是实现"虚拟构造器"的一种途径,允许声明"我不知道你的确切类型,但是无论如何都要正确创建你自己"。使用newInstance()来创建类,必须带有默认构造器。
6 Java还提供了另一种方法来生成对Class对象的引用,即使用类字面常量,如FancyToy.class。类字面常量不仅可以应用于普通的类,也可以应用于接口,数组以及基本数据类型。对于基本数据类型的包装器类,还有一个标准字段TYPE,指向对应基本数据类型的Class对象类型。建议使用".class"的形式,与普通类保持一致
...等价于... | |
boolean.class | Boolean.TYPE |
char.class | Character.TYPE |
byte.class | Byte.TYPE |
short.class | Short.TYPE |
int.class | Integer.TYPE |
long.class | Long.TYPE |
float.class | Float.TYPE |
double.class | Double.TYPE |
void.class | Void.TYPE |
7 当使用".class"来创建对Class对象的引用时,不会自动地初始化该Class对象。为了使用类而做的准备工作实际包含了三个步骤:
- 加载。由类加载器执行。该步骤将查找字节码,并从这些字节码中创建一个Class对象
- 链接。在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必需的话,将解析这个类创建的对其他类的所有引用。
- 初始化。如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块
8 如下代码所示,代码分析
Class initable = Initable.class; // 使用.class语法仅获得对类的Class对象引用并不会引发初始化,即不会输出"Initializing Initable"
System.out.println("After creating Initable ref");
System.out.println(Initable.staticFinal); // 如果一个static final值是编译器常量,如staticFinal,无须对Initable初始化即可读取
System.out.println(Initable.staticFinal2); // 但staticFinal2不是编译器常量,使用需要初始化Initable才能读取,即会输出"Initializing Initable"
System.out.println(Initable2.staticNonFinal); // 如果一个static成员变量不是final,它被读取前必须要先进行链接和初始化
try{
Class initable3 = Class.forName("c06.Initable3"); // 获得Class对象引用同时还会引发初始化
}catch(Exception e){
e.printStackTrace();
}
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
class Initable{
static final int staticFinal = 47;
static final int staticFinal2 = ArrayApp.rand.nextInt(1000);
static {
System.out.println("Initializing Initable");
}
}
class Initable2{
static int staticNonFinal = 147;
static {
System.out.println("Initializing Initable2");
}
}
class Initable3{
static int staticNonFinal = 74;
static {
System.out.println("Initializing Initable3");
}
}
public class ArrayApp {
public static Random rand = new Random(47);
public static void main(String[] args){
Class initable = Initable.class;
System.out.println("After creating Initable ref");
System.out.println(Initable.staticFinal);
System.out.println(Initable.staticFinal2);
System.out.println(Initable2.staticNonFinal);
try{
Class initable3 = Class.forName("c06.Initable3");
}catch(Exception e){
e.printStackTrace();
}
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
}
}
输出
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
9 Class引用表示的就是它所指向的对象的确切类型,而该对象便是Class类的一个对象。泛型类引用只能赋值为指向其声明的类型,编译器会强制执行额外的类型检查,如genericIntClass不能赋值double.class。而普通的类引用可以重新被赋值为指向任何其他的Class对象,如intClass
Class intClass = int.class;
Class<Integer> genericIntClass = int.class;
genericIntClass = Integer.class;
intClass = double.class;
genericIntClass = double.class; // error
10 因为Integer继承于Number,但Integer Class对象并不是Number Class对象的子类。可以使用通配符"?"来说放松限制。
Class<Number> g = int.class; // error
Class<?> k = int.class; // ok
k = double.class; // ok
11 为了创建一个Class引用,且限定为某种类型或该类型的任何子类型,可以将通配符与extends关键字结合,创建一个范围
Class<? extends Number> k = int.class;
k = double.class;
k = Number.class;
12 Class引用的转型语法,即cast()函数
class Building{}
class House extends Building{}
public class ArrayApp {
public static void main(String[] args){
Building b = new House();
Class<House> houseType = House.class;
House h = houseType.cast(b); // 等价于h = (House)b
h = (House)b;
}
}
13 RTTI在Java中还有第三种形式,即关键字instanceof,返回一个布尔值,判断对象是否为某个类型的实例。如下所示,在将x转型为一个Dog前,先判断对象x是否从属于Dog类。
if(x instanceof Dog)
((Dog)x).bark();
14 工厂方法设计模式 ?
15 instanceof和isInstance()判断结果一样,equals()和==判断结果也一样。instanceof保持了类型的概念,它表示“你是这个类吗,或者这个类的子类吗”
class Base{}
class Derived extends Base{}
public class ArrayApp {
static void test(Object x){
System.out.println("Testing x of type "+x.getClass());
System.out.println("x instanceof Base "+(x instanceof Base));
System.out.println("x instanceof Derived " +(x instanceof Derived));
System.out.println("Base.isInstance(x) "+Base.class.isInstance(x));
System.out.println("Derived.isInstance(x) "+Derived.class.isInstance(x));
System.out.println("x.getClass() == Base.class "+(x.getClass() == Base.class));
System.out.println("x.getClass() == Derived.class "+(x.getClass() == Derived.class));
System.out.println("x.getClass().equals(Base.class) "+(x.getClass().equals(Base.class)));
System.out.println("x.getClass().equals(Derived.class) "+(x.getClass().equals(Derived.class)));
}
public static void main(String[] args){
test(new Base());
test(new Derived());
}
}
输出
Testing x of type class c06.Base
x instanceof Base true
x instanceof Derived false
Base.isInstance(x) true
Derived.isInstance(x) false
x.getClass() == Base.class true
x.getClass() == Derived.class false
x.getClass().equals(Base.class) true
x.getClass().equals(Derived.class) false
Testing x of type class c06.Derived
x instanceof Base true
x instanceof Derived true
Base.isInstance(x) true
Derived.isInstance(x) true
x.getClass() == Base.class false
x.getClass() == Derived.class true
x.getClass().equals(Base.class) false
x.getClass().equals(Derived.class) true
16 反射,Class的getMethods()和getConstructors()函数分别返回Method对象数组和Constructor对象数组
public class ArrayApp {
private static String usage = "usage:\n"
+"ShowMethods qualified.class.name\n"+
"To show all methods in class or:\n"+
"ShowMethods qualified.class.name word\n"+
"To search for methods involving 'word'";
private static Pattern p = Pattern.compile("\\w+\\.");
public static void main(String[] args){
if(args.length < 1) {
System.out.println(usage);
System.exit(0);
}
int lines = 0;
try{
Class<?> c = Class.forName(args[0]);
Method[] methods = c.getMethods();
Constructor[] ctors = c.getConstructors();
if(args.length == 1){
for(Method m:methods)
System.out.println(p.matcher(m.toString()).replaceAll(""));
for(Constructor ct:ctors)
System.out.println(p.matcher(ct.toString()).replaceAll(""));
lines = methods.length + ctors.length;
}else {
for(Method m:methods)
if(m.toString().indexOf(args[1]) != -1){
System.out.println(p.matcher(m.toString()).replaceAll(""));
lines++;
}
for(Constructor ct:ctors)
if(c.toString().indexOf(args[1]) != -1){
System.out.println(p.matcher(ct.toString()).replaceAll(""));
lines++;
}
}
}catch(ClassNotFoundException e){
System.out.println("No such class: "+e);
}
}
}
17 代理是基本的设计模式之一,它是为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。这些操作通常涉及与“实际”对象的通信,因此代理通常充当着中间人的角色。