Thinking in Java
From Thinking in Java 4th Edition
除了static方法(它是针对类调用的,并不依赖于对象的存在),方法只有通过对象才能被调用,且这个对象必须能执行这个方法调用。
当声明一个事物是static时,就意味着这个域或方法不会与包含它的那个类的任何对象实例关联在一起。
有一个特定类会自动被导入到每一个Java文件中:java.lang.
为对象赋值时,情况发生了变化(不同于C++具有赋值构造函数)。对一个对象进行操作时,我们真正操作的是对象的引用。若“将一个对象赋值给另一个对象”,实际是将“引用”从一个地方复制到另一个地方。这意味着如果对对象使用c=d,那么c和d都指向原本只有d指向的那个而对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import static net.mindview.util.Print.*; class Tank{ int level; } public class Assignment{ public static void main(String[] args){ Tank t1 = new Tank(); Tank t2 = new Tank(); t1.level = 9 ; t2.level = 47 ; print( "1: t1.level: " + t1.level + ", t2.level: " + t2.level); t1 = t2; print( "2: t1.level: " + t1.level + ", t2.level: " + t2.level); t1.level = 27 ; print( "3: t1.level: " + t1.level + ", t2.level: " + t2.level); } } /* Output: 1: t1.level: 9, t2.level: 47 2: t1.level: 47, t2.level: 47 3: t1.level: 27, t2.level: 27 */ |
将一个对象传递给方法时,也会产生别名问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import static net.mindview.util.Print.*; class Letter{ char c; } public class PassObject{ static void f(Letter y){ y.c = 'z' ; } public static void main(String[] args){ Letter x = new Letter(); x.c = 'a' ; print( "1: x.c: " + x.c); f(x); print( "2: x.c: " + x.c); } } /* Output 1: x.c: a 2: x.c: z */ |
equals()的默认行为是比较引用。所以除非在自己的新类中覆盖equals()方法,否则不可能表现出我们希望的行为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Value{ int i; } public class EqualsMethod2{ public static void main(String[] args){ Value v1 = new Value(); Value v2 = new Value(); v1.i = v2.i = 100 ; System.out.println(v1.equals(v2)); } } /* Output false */ |
移位操作
左移位操作符(<<):左移,低位补0
“有符号”右移位操作符(>>):使用“符号扩展”,若符号为正,高位插入0,若符号为负,高位插入1.
“无符号”右移位操作符(>>>):“零扩展”,无论正负,高诶插入0.
如果表达式以一个字符串起头,那么后续所有操作数都必须是字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import static net.mindview.util.Print.*; public class StringOperators{ public static void main(String[] args){ int x = 0 , y = 1 , z = 2 ; String s = "x, y, z " ; print(s + x + y + z); print(x + " " + s); // Convert x to a String s += "(summed) = " ; // Concatenation operator print(s + (x + y + z)); print( "" + x); // Shorthand for Integer.toString() } } /* Output x, y, z 012 0 x, y, z x, y, z(summed) = 3 0 */ |
foreach用于数组和容器。foreach语法表示不必创建int变量去对由访问项构成的序列进行计数,foreach将自动产生每一项
1 2 3 4 5 6 7 8 9 10 11 12 13 | import java.util.*; public class ForEachFloat{ public static void main(String[] args){ Random rand = new Random( 47 ); float f[] = new float [ 10 ]; for ( int i = 0 ; i < 10 ; ++i) f[i] = rand.nextFloat(); for ( float x : f) System.out.println(x); } } |
1 2 3 4 5 6 7 8 | public class ForEachString{ public static void main(String[] args){ for ( char c : "An African Swallow" .toCharArray()) System.out.println(c + " " ); } } /* A n A f r i c a n S w a l l o w */ |
方法重载:方法名相同,每个重载的方法有独一无二的参数类型列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import static net.mindview.util.Print.*; class Tree{ int height; Tree(){ print( "Planting a seedling" ); height = 0 ; } Tree( int initialHeight){ height = initialHeight; print( "Creating new Tree that is " + height + " feet tall" ); } void info(){ print( "Tree is " + height + " feet tall" ); } void info(String s){ print(s + ": Tree is " + height + " feet tall" ); } } public class OverLoading{ public static void main(String[] args){ for ( int i = 0 ; i < 5 ; ++i){ Tree t = new Tree(i); t.info(); t.info( "overloaded method" ); } // Overloaded constructor: new Tree(); } } |
如果已经定义了一个构造器(无论是否有参数),编译器就不会帮你自动创建默认构造器:
1 2 3 4 5 6 7 8 9 10 11 12 | class Bird2{ Bird2( int i){} Bird2( double d) {} } public class NoSynthesis{ public static void main(String[] args){ //! Bird2 b = new Bird2(); // No default Bird2 b = new Bird2( 1 ); Bird2 b = new Bird2( 1.0 ); } } |
this关键字对于将当前对象传递给其他方法也很有用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class Person{ public void eat(Apple apple){ Apple peeled = apple.getPeeled(); System.out.println( "Yummy" ); } } class Peeler{ static Apple peel(Apple apple){ // ... remove peel return apple; // Peeled } } class Apple{ Apple getPeeled(){ return Peeler.peel( this ); } } public class PassingThis{ public static void main(String[] args){ new Person.eat( new Apple()); } } |
构造器中调用构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | import static net.mindview.util.Print.*; public class Flower{ int petalCount = 0 ; String s = "initial value" ; Flower( int petals){ petalCount = petals; print( "Constructor w/ int arg only, petalCount = " + petalCount); } Flower(String ss){ print( "Constructor w/ String arg only, s = " + ss); s = ss; } Flower(String s, int petals){ this (petals); //! this(s); // Can't call two! this .s = s; // Another use of "this" print( "String & int args" ); } Flower(){ this ( "hi" , 47 ); print( "default constructor (no args)" ); } void printPetalCount(){ //! this(11); // Not inside non-constructor! print( "petalCount = " + petalCount + " s = " + s); } public static void main(String[] args){ Flower x = new Flower(); x.printPetalCount(); } } /* Output Constructor w/ int arg only, petalCount = 47 String & int args default constructor (no args) petalCount = 47 s = hi */ |
finalize()能在垃圾回收时做一些重要的清理工作。例如,假设某个对象在创建过程中会将自己绘制到屏幕上,如果不是明确地从屏幕上将其擦除,它可能永远得不到清理。如果在finalize()里加入某种擦除功能,当“垃圾回收”发生时(不能保证一定会发生),finalize()得到了调用,图像就会被擦除。然而,如果“垃圾回收”没有发生,图像就会一直被保留下来。
finalize()可能的使用方法,对象终结条件的验证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | class Book{ boolean checkedOut = false ; Book( boolean checkOut){ checkOut = checkOut; } void checkIn(){ checkedOut = false ; } protected void finalize(){ System.out.println( "Error: checked out" ); // Normally, you'll also do this: // super.finalize(); // Call the base-class version } } public class TerminationCondition{ public static void main(String[] args){ Book novel = new Book( true ); // Proper cleanup novel.checkIn(); // Drop the reference, forget to clean up new Book( true ); // Force garbage collection & finalization System.gc(); } } /* Output: Error: checked Out */ |
垃圾回收依据的思想是:对任何“活”的对象,一定能最终追溯到其存活在堆栈或静态存储区之中的引用。由此,如果从堆栈和静态存储区开始,遍历所有的引用,就能找到所有“活”的对象。(对于发现的每个引用,必须追踪它所引用的对象,然后是此对象包含的所有引用,如此反复进行,知道“根源于堆栈和静态存储区的引用”所形成的网络全部被访问为止。)
类的每个基本类型数据成员保证都有一个初始值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | import static net.mindview.util.Print.*; public class InitialValues{ boolean t; char c; byte b; short s; int i; long l; float f; double d; InitialValues reference; void printInitialValues(){ print( "Data type Initial value" ); print( "boolean " + t); print( "char [" + c + "]" ); print( "byte " + b); print( "short " + s); print( "int " + i); print( "long " + l); print( "float " + f); print( "double " + d); print( "reference " + reference); } public static void main(String[] args){ InitialValue iv = new InitialValues(); iv.printInitialValues(); /* You could also say: new InitialValues().printInitialValues(); */ } } /* Output: Data type Initial value boolean false char [ ] byte 0 short 0 int 0 long 0 float 0.0 double 0.0 reference null */ |
Java可以在定义类成员变量的地方为其赋值,而C++则不行,C++中必须在构造函数中完成初始化赋值
1 2 3 4 5 6 7 8 | public class InitialValues2{ boolean bool = true ; char ch = 'x' ; byte b = 47 ; short s = 0xff ; int i = 999 ; long lng = 1 ; } |
初始化顺序
在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。(即:变量先被初始化,然后才调用构造器)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import static net.mindview.util.Print.*; class Window{ Window( int marker) {print( "Window()" + marker + ")" );} } class House{ Window w1 = new Window( 1 ); // Before constructor; House(){ // Show that we're in the constructor print( "House()" ); w3 = new Window( 33 ); // Reinitialize w3 } Window w2 = new Window( 2 ); // After constructor void f(){ print( "f()" ); } Window w3 = new Window( 3 ); // At end } public class OrderOfInitialization{ public static void main(String[] args){ House h = new House(); h.f(); // Shows that construction is done } } /*Output: Window(1) Window(2) Window(3) House() Window(33) f() */ |
静态存储区域初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | import static net.mindview.util.Print.*; class Bowl{ Bowl( int marker){ print( "Bowl()" + marker + ")" ); } void f1( int marker){ print( "f1()" + marker + ")" ); } } class Table{ static Bowl bowl1 = new Bowl( 1 ); Table(){ print( "Table()" ); bowl2.f1( 1 ); } void f2( int marker){ print( "f2(" + marker + ")" ); } static Bowl bowl2 = new Bowl( 2 ); } class Cupboard{ Bowl bowl3 = new Bowl( 3 ); static Bowl bowl4 = new Bowl( 4 ); Cupboard(){ print( "Cupboard()" ); bowl4.f1( 2 ); } void f3( int marker){ print( "f3(" + marker + ")" ); } static Bowl bowl5 = new Bowl( 5 ); } public class StaticInitialization{ public static void main(String[] args){ print( "Creating new Cupboard() in main" ); new Cupboard(); print("Creating new Cupboard() in main; new Cupboard(); table.f2( 1 ); cupboard.f3( 1 ); } static Table table = new Table(); static Cupboard cupboard = new Cupboard(); } /*Output: Bowl(1) Bowl(2) Table() f1(1) Bowl(4) Bowl(5) Bowl(3) Cupboard() f1(2) Creating new Cupboard() in main Bowl(3) Cupboard() f1(2) Creating new Cupboard() in main Bowl(3) Cupboard() f1(2) f2(1) f3(1) */ |
1. StaticInitialization中的static new Table()
1.1 static new Bowl(1) .....
2. StaticInitialization中的static new Cupboard() ......
3. StaticInitialization中的main() ........
静态块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | import static net.mindview.util.Print.*; class Cup{ Cup( int marker){ print( "Cup(" + marker + ")" ); } void f( int marker){ print( "f(" + marker + ")" ); } } class Cups{ static Cup cup1; static Cup cup2; static { cup1 = new Cup( 1 ); cup2 = new Cup( 2 ); } Cups(){ print( "Cups()" ); } } public class ExplicitStatic{ public static void main(String[] args){ print( "Inside main()" ); Cups.cup1.f( 99 ); // (1) } // static Cups cups1 = new Cups(); // (2) // static Cups cups2 = new Cups(); // (3) } /*Output: Inside main() Cup(1) Cup(2) f(99) */ |
非静态实例初始化
用于初始化每一个对象的非静态变量,在构造器之前执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | import static net.mindview.util.Print.*; class Mug{ Mug( int marker){ print( "Mug(" + marker + ")" ); } void f( int marker){ print( "f(" + marker + ")" ); } } public class Mugs{ Mug mug1; Mug mug2; { mug1 = new Mug( 1 ); mug2 = new Mug( 2 ); print( "mug1 & mug2 initialized" ); } Mugs(){ print( "Mugs()" ); } Mugs( int i){ print( "Mugs(int)" ); } public static void main(String[] args){ print( "Inside main()" ); new Mugs(); print( "new Mugs() completed" ); new Mugs( 1 ); print( "new Mugs(1) completed" ); } } /*Output: Inside main() Mug(1) Mug(2) mug1 & mug2 initialized Mugs() new Mugs() completed Mug(1) Mug(2) mug1 & mug2 initialized Mugs(int) new Mugs(1) completed */ |
创建数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import java.util.*; import static net.mindview.util.Print.*; public class ArrayNew{ public static void main(String[] args){ int [] a; Random rand = new Random( 47 ); a = new int [rand.nextInt( 20 )]; print( "length of a = " + a.length); print(Arrays.toString(a)); } } /*Output: length of a = 18 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] */ |
枚举类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public enum Spiciness{ NOT, MILD, MEDIUM, HOT, FLAMING } public class SimpleEnumUse{ public static void main(String[] args){ Spiciness howHot = Spiciness.MEDIUM; System.out.println(howHot); } } /*Output: MEDIUM */ public class EnumOrder{ public static void main(String[] args){ for (Spiciness s : Spiciness.values()) System.out.println(s + ", ordinal " + s.ordinal()); } } /*Output: NOT, ordinal 0 MILD, ordinal 1 MEDIUM, ordinal 2 HOT, ordinal 3 FLAMING, ordinal 4 */ |
enum可以在switch内部使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public class Burrito{ Spiciness degree; public Burrito(Spiciness degree){ this .degree = degree;} public void describe(){ System.out.print( "This burrito is " ); switch (degree){ case NOT: System.out.println( "not spicy at all." ); break ; case MILD: case MEDIUM: System.out.println( "a little hot." ); break ; case HOT: case FLAMMING: default : System.out.println( "maybe too hot." ) } } public static void main(String[] args){ Burrito plain = new Burrito(Spiciness.NOT), greenChile = new Burrito(Spiciness.MEDIUM), jalapeno = new Burrito(Spiciness.HOT); plain.describe(); greenChile.describe(); jalapeno.describe(); } } /* Output: This burrito is not spicy at all. This burrito is a little hot. This burrito is maybe too hot. */ |
Java源文件代码,通常被称为编译单元。每个编译单元都必须有一个.java后缀。要求:
1. 一个public类,且该类的名称必须与文件名相同。
2. 只能有一个public类。
3. 别的类不可以是public,作为对主public类的支持。
package access;
表示你在声明该编译单元是名为access的类库的一部分,或者说,你正在声明该编译单元中的public类名称是位于access名称的保护伞下。任何想要使用该名称的人,都必须使用指定全名或者与access结合使用关键字imort。
Define:
1 2 3 4 5 | package access.mypackage; public class MyClass{ // ... } |
Use:
1 2 3 4 5 | public class QualifiedMyClass{ public static void main(String[] args){ access.mypackage.MyClass m = new access.mypackage.MyClass(); } } |
Or be simple:
1 2 3 4 5 6 7 | import access.mypackage.*; public class ImportedMyClass{ public static void main(String[] args){ MyClass m = new MyClass(); } } |
运用CLASSPATH来分隔:
两个文件的名字空间在net.mindview.simple之下:
1 2 3 4 5 6 | //: net/mindview/simple/Vector.java package net.mindview.simple; public class Vector{ public Vector(){<br> System.out.println( "net.mindview.simple.Vector" );<br> } } |
and
1 2 3 4 5 6 7 8 | //: net/mindview/simple/List.java package net.mindview.simple; public class List{ public List(){ System.out.println( "net.mindview.simple.List" ); } } |
这两个文件居位于目录:C:\DOC\JAVAT\net\mindview\simple
CLASSPATH的设置: CLASSPATH=.;D:\JAVA\LIB;C:\DOC\JavaT
静态import语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | //: net/mindview/util/Print.java package net.mindview.util; import java.io.*; public class Print{ // Print with a newline: public static void print(Object obj){ System.out.println(obj); } // Print a newline by itself: public static void print(){ System.out.println(); } // Print with no line break: public static void printnb(Object obj){ System.out.print(obj); } // The new Java SE5 printf() (from C): public static PrintStream printf(String format, Object... args){ return System.out.printf(format, args); } } |
编译完成后,可以用import static语句在你的系统上使用静态的print()和printnb()方法了。
range()方法的构建:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | //: net/mindview/util/Range.java package net.mindview.util; public class Range{ // Produce a sequence of [0...n) public static int [] range( int n){ int [] result = new int [n]; for ( int i = 0 ; i < n; ++i) result[i] = i; return result; } // Produce a sequence [start...end) public static int [] range( int start, int end){ int sz = end - start; int [] result = new int [sz]; for ( int i = 0 ; i < sz; ++i) result[i] = start + i; return result; } // Produce a sequence [start...end) incrementing by step public static int [] range( int start, int end, int step){ int sz = (end - start) / step; int [] result = new int [sz]; for ( int i = 0 ; i < sz; ++i) result[i] = start + (i * step); return result; } } |
包的使用必须遵循:
1. 创建的包都已经在给定的名称的时候隐含地指出了目录结构
2. 这个包必须位于其名称所指定的目录之中
3. 该目录必须是在以CLASSPATH开始的目录中可以查询到
private示例
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Sundae{ private Sundae(){} static Sundae makeASundae(){ return new Sundae(); } } public class IceCream{ public static void main(String[] args){ //! Sundae x = new Sundae(); Sundae x = Sundae.makeASundae(); } } |
protecte示例
1 2 3 4 5 6 7 8 9 10 11 12 | //: access/cookie2/Cookie.java package access.cookie2; public class Cookie{ public Cookie(){ System.out.println( "Cookie constructor" ); } protected void bite(){ System.out.println( "bite" ); } } |
这样对继承类,bite()就可以被使用了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //: access/ChocolateChip2.java import access.cookie2.*; public class ChocolateChip2 extends Cookie{ public ChocolateChip2(){ System.out.println( "ChocolateChip2 constructor" ); } public void chomp(){ bite(); } // Protected method public static void main(String[] args){ ChocolateChip2 x = new ChocolateChip2(); x.chomp(); } } /* Output: Cookie constructor ChocolateChip2 constructor bite */ |
类的访问权限,仅有两个选择:包访问权限或public。类不可以是private的,因为那样会使得除了该类之外,其他任何类都不可以访问它,也不可以是protected(内部类既可以是private也可以是protected)。如果不希望其他任何人对该类拥有访问权,可以把所有的构造器都指定为private。但有一个例外,该类的static成员内部可以创建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | class Soup1{ private Soup1(){} // (1) Allow creation via static method: public static Soup1 makesoup(){ return new Soup1(); } } class Soup2{ private soup2(){} // (2) Create a static object and return a reference // upon request. (The "Singleton" pattern) private static Soup2 ps1 = new Soup2(); public static Soup2 access(){ return ps1; } public void f(){} } // Only one public class allowed per file: public class Lunch{ void testPrivate(){ // Can't do this! Private constructor: //! Soup1 soup = new Soup1(); } void testStatic(){ Soup1 soup = Soup1.makeSoup(); } void testSingleton(){ Soup2.access().f(); } } |
继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | import static net.mindview.util.Print.*; class Cleanser{ private String s = "Cleanser" ; public void append(String a) {s += a;} public void dilute(){append( " dilute()" );} public void apply(){ append( " apply()" );} public void scrub(){ append( " scrub()" );} public String toString(){ return s;} public static void main(String[] args){ Cleanser x = new Cleanser(); x.dilute(); x.apply(); x.scrub(); print(x); } } public class Detergent extends Cleanser{ // Change a method: public void scrub(){ append( " Detergent.scrub()" ); super .scrub(); // Call base-class version } // Add method to the interface: public void foam(){ append( " foam()" );} // Test the new class: public static void main(String[] args){ Detergent x = new Detergent(); x.dilute(); x.apply(); x.scrub(); x.foam(); print(x); print( "Testing base class:" ); Cleanser.main(args); } } /* Output: Cleanser dilute() apply() Detergent.scrub() scrub() foam() Testing base class: Cleanser dilute() apply() scrub() */ |
Java会自动在导出类的构造器中插入对基类构造器的调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import static net.mindview.util.Print.*; class Art{ Art(){ print( "Art constructor" );} } class Drawing extends Art{ Drawing(){ print( "Drawing constructor" );} } public class Cartoon extends Drawing{ public Cartoon(){ print( "Cartoon constructor" );} public static void main(String[] args){ Cartoon x = new Cartoon(); } } /*Output: Art constructor Drawing constructor Cartoon constructor */ |
调用带参数的基类构造器,就必须使用关键字super显示地编写调用基类构造器的语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import static net.mindview.util.Print.*; class Game{ Game( int i){ print( "Game constructor" ); } } class BoardGame extends Game{ BoardGame( int i){ super (i); print( "BoardGame constructor" ); } } public class Chess extends BoardGame{ Chess(){ super ( 11 ); print( "Chess constructor" ); } public static void main(String[] args){ Chess x = new Chess(); } } /* Output: Game constructor BoardGame constructor Chess constructor */ |
调用基类构造器必须是你在导出类构造器中要做的第一件事。
代理
将一个成员对象置于所要构造的类中(就像组合),但与此同时我们在新类中暴露了该成员对象的所有方法(就像继承)
1 2 3 4 5 6 7 8 9 10 11 | public class SpaceShipControls{ void up( int velocity) {} void down( int velocity) {} void left( int velocity) {} void right( int velocity) {} void forward( int velocity) {} void back( int velocity) {} vjoid turboBoost() {} } |
构造太空飞船的一种方式是继承
1 2 3 4 5 6 7 8 9 10 | public class SpaceShip extends SpaceShipControls{ private String name; public SpaceShip(String name) { this .name = name;} public String toString() { return name;} public static void main(String[] args){ SpaceShip protector = new SpaceShip( "NSEA Protector" ); protector.forward( 100 ); } } |
然而SpaceShip并不是SpaceShipControls。准确地讲应该是:SpaceShip包含了SpaceShipControls,与此同时,SpaceShipControls的所有方法在SpaceShip中都暴露了出来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | public class SpaceShipControls{ void up( int velocity) {} void down( int velocity) {} void left( int velocity) {} void right( int velocity) {} void forward( int velocity) {} void back( int velocity) {} vjoid turboBoost() {} } public class SpaceShip extends SpaceShipControls{ private String name; public SpaceShip(String name) { this .name = name;} public String toString() { return name;} public static void main(String[] args){ SpaceShip protector = new SpaceShip( "NSEA Protector" ); protector.forward( 100 ); } } public class SpaceShipDelegation{ private String name; private SpaceShipControls controls = new SpaceShipControls(); public SpaceShipDelegation(String name){ this .name = name; } // Delegated methods: public void back( int velocity){ controls.back(velocity); } public void down( int velcoity){ controls.down(velocity); } public void forward( int velocity){ controls.forward(velocity); } public void left( int velocity){ controls.left(velocity); } public void right( int velocity){ controls.right(velocity); } public void turboBoost(){ controls.turboBoost(); } public void up( int velocity){ controls.up(velocity); } public static void main(String[] args){ SpaceShipDelegation protector = new SpaceShipDelegation( "NSEA Protector" ); protector.forward( 100 ); } } |
导出类重新定义基类的重载方法,不会屏蔽基类的任何版本(与C++不同)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import static net.mindview.util.Print.*; class Homer{ char doh( char c){ print( "doh(char)" ; return 'd' ; } float doh( float f){ print( "doh(float)" ); return 1 .0f; } } class Milhouse{} class Bart extends Homer{ void doh(Milhouse m){ print( "doh(Milhouse)" ); } } public class Hide{ public static void main(String[] args){ Bart b = new Bart(); b.doh( 1 ); b.doh( 'x' ); b.doh( 1 .0f); b.doh( new Milhouse()); } } /* Output: doh(float) doh(char) doh(float) doh(Milhouse) */ |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)