初始化与清理
参数的顺序可以区分两个函数
不能根据返回值来区分两个函数如果传入的数据类型小于方法中声明的数据类型,实际数据类型就会被提升
如果无法找到接受char型参数的方法,就会把char直接提升为Int型
如果传入的实际参数过大,就要强制转化,不然会报错如果类中没有构造器,编译器会创建一个默认构造函数
如果定义了,无论是否有参,就不会给你创建,如果你只写了有参的,无参的构造函数就没有了java中的成员函数也像C++中的一样隐含着一个对象参数,但在java中用不上
常常需要返回自身对象时,或者需要自身对象作为函数参数时,需要用this来代替对象
this对象可以辅助写多个构造函数(但必须放在开头),可以避免类参数和传入参数产生歧义static方法没有this对象
static方法中不能调用非stati方法java允许在类中定义一个finalize()函数,一旦垃圾回收器准备少释放对象所占的内存是,将首先调用finalize()方法,java有垃圾回收器,之所以要有此方法是为了针对调用非java程序(例如C程序)时产生的内存泄漏的情况。finalize可以用来当成发现错误的工具(有点类似assert),在程序最终结束时进行终结条件的验证
对于局部变量,如果没有初始化而被使用(即使是基本数据类型)会抛出编译时异常
但是类中的每个基本类型数据成员都会保证有一个初始值
注意:无法类中变量自动初始化的运行,它将在构造函数被调用之前发生
变量定义的先后顺序决定了初始化的顺序,即使变量定义散布于方法定义之间,它们仍旧会在任何方法被调用之前得到初始化只有第一次访问静态数据的时候才会被创建,此后静态对象不会再次被初始化
初始化的顺序是先静态对象,而后是“非静态”对象
即使没有显示的static关键字,构造函数实际上也是静态的数组定义的两种写法:
int[] a1;int a1[];
Integer[] a = {new Integer(1),new Integer(2)};这中初始化只能用在定义处可变参数列表
Object… args//传参的时候也可以不传,Object也可以变成其他的类型,包括基本类型
由于可以基本类型和包装类型可以转换(自动装箱),不带参数地或者参数可转化地调用有可变参数的函数,可能会产生歧义(不知道调用哪个,因为每个都可以通过自动装箱或者类型转化匹配上)
访问权限
- 权限排名:public、protected、包访问权限、private
protected也提供包访问权限 - 每个编译单元都只能有一个public类,这个类的名字要和文件名一致,没有public就不作要求
- 类既不可以是private的也不可以是protected的(内部类除外)
- 相同目录下所有不明确package声明的文件,都被视为是该目录下的默认包的一部分
复用类
- 复用包括:组合、继承、代理
- 初始化变量的四种方式:
1 在定义对象的地方,也就是定义为类成员的时候(这时候的初始化会在构造函数之前,从侧面也说明了,在构造函数初始化变量的时候,其实变量已经在之前初始化了一次)
2 在类的构造函数中
3 在正要使用该变量之前,这也成为惰性初始化
4 实例初始化(直接赋值) - 对于默认构造函数,Java会自动在子类的构造函数中调用基类的构造函数;但是如果构造函数是有参的,就要显式地用super来调用
- 代理:使用了组合(类中定义了包含了某个对象),但是我们也需要这个对象的方法(成员对象一般是private的不能用),于是就定义与该对象方法同名的方法,然后在方法体中是用这个对象调用同名的方法。为什么不直接用继承呢?这样方法不都有了吗,因为继承关系太近了,所有的方法都方法都暴露给了我们创建的对象。所谓代理,就是我要用你的方法,但是你不想和我有亲缘关系,那就我一旦想做某件事就让你来做,我在旁边看着,而不是我和你一起来做这件事。
- 子类重新定义的该方法名称并不会屏蔽其在基类中的任何版本,当然签名相同的肯定覆盖了
- final数据:
对于非基本类型,final只是保证引用的不变
final数据可以运行时确定,比如调用random方法产生的数据
java中可以定义final数据,而不初始化,并且针对不同的对象,初始化为不同的final数据,也称为空白final - final参数
将无法更改参数引用所指向的对象 - final方法
可以防止子类修改
类中所有private方法都是隐式为final方法 - final类,不能继承
多态
- 多态的缺陷:
1 私有方法不能被覆盖,基类的私有方法在子类是不可见,所以多态用不了(重载不了)
2 某个方法是静态的,其行为就不具有多态性
3 域访问问题:多态时,直接访问域当然是访问的是基类的(编译时确定的,前提是可访问),但是如果有某个重载的函数,函数里面又访问域,这时访问的是子类的域 - 构造函数和多态
在基类的构造函数中,如果没有明确指定调用某个基类构造函数, 他就会默默地调用默认构造函数。如果不存在默认构造函数, 编译器就会报错 - 构造函数里面调用多态的方法,结果可能是灾难性的,因为,有可能在子类对象还没构造完全,就使用了子类的多态方法
在构造器内唯一能够安全调用的的那些方法是基类的final方法。这些方法不会覆盖,因袭也不会出现上面的问题 - 继承和组合的选择规则
用继承表达行为间的差异,用字段表达状态上的变化
接口
- 包含抽象方法的是抽象类
继承自抽象类不去实现抽象方法,将会被强制限定该类为抽象类 - 接口也可以包含域,但默认都是static和final的,因此可以用来定义常量
- 接口中的方法即使不写访问权限也是默认为public的
- 接口中的变量可以被非常量表达式初始化,比如Random类
- 当实现某个接口时,并不需要实现嵌套在其内部的任何接口,而且,pirvate接口不能在定义它的类之外被实现
内部类
- 内部类拥有外部类的所有访问权限
- 内部类有一个指向外部类的引用
- 使用外部类的引用:outer.this
- new一个内部类:outer.new inerclass();
- 拥有外部类之前是不能创建内部类对象的
- 如果创建的是静态内部类,不需要外部类对象的引用
容器
- Collection 一个独立的元素序列
Map 一组成对的键值对对象 - 如果需要子类的独有方法,定义List或者Map时,就不要使用子类的类型
- Arrays.asList()可以将一个数组或是一个用逗号分隔的元素列表转换为一个List对象,注意返回的对象大小不能修改,不然会出错(内部是使用了一个数组存储)
Collection.addAll()比Arrays.asList()用来构造一个Collection要快 - 构建一个不包含元素的Collection,然后调用Colections.addAll()这种方式很方便。
//snow是所有类的基类
List<Snow> snow1 = Arrays.asList(
new Crusty(), new Slush(), new Powder());
// 不行,因为Powder才是这两个类最近的基类
List<Snow> snow2 = Arrays.asList(
// new Light(), new Heavy());
// Compiler says:
// found : java.util.List<Powder>
// required: java.util.List<Snow>
//可以工作,实际上该方法从第一个参数中就了解了要放什么类型
List<Snow> snow3 = new ArrayList<Snow>();
Collections.addAll(snow3, new Light(), new Heavy());
//直接添加显式参数
List<Snow> snow4 = Arrays.<Snow>asList(
new Light(), new Heavy());
- 注意List的indexOf、contains、remove依赖于equals的实现
- Iterator可以移除由next()产生的最后一个元素,这意味着在调用remove之前必须先调用next()(不是当前指向的元素,上一次next调用后指向的元素);
- LinkedList能够实现栈的所有功能同时也实现了Queue的接口,也可以拿来实现Stack.
- Set有与Collection完全一样的接口 TreeSet使用了红黑树,HashSet使用的是散列函数
- 任何实现Iterable接口的类,都可以用在foreach中,Map没有,Collection有
增加Iterable的功能代码:
class ReversibleArrayList<T> extends ArrayList<T> {
public ReversibleArrayList(Collection<T> c) { super(c); }
public Iterable<T> reversed() {
return new Iterable<T>() {
public Iterator<T> iterator() {
return new Iterator<T>() {
int current = size() - 1;
public boolean hasNext() { return current > -1; }
public T next() { return get(current--); }
public void remove() { // Not implemented
throw new UnsupportedOperationException();
}
};
}
};
}
}
public class AdapterMethodIdiom {
public static void main(String[] args) {
ReversibleArrayList<String> ral =
new ReversibleArrayList<String>(
Arrays.asList("To be or not to be".split(" ")));
// Grabs the ordinary iterator via iterator():
for(String s : ral)
System.out.print(s + " ");
System.out.println();
// Hand it the Iterable of your choice
for(String s : ral.reversed())
System.out.print(s + " ");
}
}
- new ArrayList(Arrays.asList(ia))和 Arrays.asList(ia)返回后的数组修改后,前者不改变ia,后者改变
异常处理
- 使用logging工具将输出记录到日志中
class LoggingException extends Exception {
private static Logger logger =
Logger.getLogger("LoggingException");
public LoggingException() {
StringWriter trace = new StringWriter();
printStackTrace(new PrintWriter(trace));
logger.severe(trace.toString());
}
}
public class LoggingExceptions {
public static void main(String[] args) {
try {
throw new LoggingException();
} catch(LoggingException e) {
System.err.println("Caught " + e);
}
try {
throw new LoggingException();
} catch(LoggingException e) {
System.err.println("Caught " + e);
}
}
} /* Output: (85% match)
Aug 30, 2005 4:02:31 PM LoggingException <init>
SEVERE: LoggingException
at LoggingExceptions.main(LoggingExceptions.java:19)
Caught LoggingException
Aug 30, 2005 4:02:31 PM LoggingException <init>
SEVERE: LoggingException
at LoggingExceptions.main(LoggingExceptions.java:24)
Caught LoggingException
*///:~
- printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问
- 重新抛出异常,更新异常的信息可以用fillInStackTrace()(抛出另一种异常,不会带有原来异常的信息)
- 连接异常链用initCause()
- 运行时异常不用手动try catch
- finally块总会执行,即使之前有return,break出现的情况下
字符串
- String是不可变的,每一个看起来像是修改了String值的方法,实际上都是创建了一个全新的String对象
- 避免toString的递归(输出对象地址的时候)
public class InfiniteRecursion {
public String toString() {
//这里面用字符串连接this是执行toString,因此为无限递归,应该用super.toString()
return " InfiniteRecursion address: " + this + "\n";
}
public static void main(String[] args) {
List<InfiniteRecursion> v =
new ArrayList<InfiniteRecursion>();
for(int i = 0; i < 10; i++)
v.add(new InfiniteRecursion());
//执行toString方法
System.out.println(v);
}
}
- System.out.format()方法可用于PrintStream或PrintWriter对象,该方法是模仿C的printf(),实际上内部是使用Formatter类实现的,String.format()方法也是如此
- 正则表达式
正则表达式插入一个普通的反斜线,应该这样“\\”,插入+号,应该用“\+”
常见的使用:String.matches(“正则”) String.split(“正则”),String.replaceFirst,String.replaceAll()
类型信息
- 运行时类型信息可以让你在程序运行时发现和使用类型的信息
- 当程序创建第一个对类的静态成员的引用时,就会加载这个类。这个证明构造器也是类的静态方法。
- getClass() 是object中的方法
- class.forName()参数要包含包名
- 使用newInstance()创建的类,必须带有默认的构造器
- 建议使用“.class”的方式获得Class对象的引用,因为不用放在try-catch中
- 仅使用.class语法来获得对类的引用不会引发初始化,但是Class.forName()立即就进行了初始化(指静态的值和方法)
- 对于static final的值是“编译器常量”,不需要通过初始化就可以读取(通过产生随机数生成的static final的值除外)
- 类型不会延续类的继承关系:
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
}
}
但是不能这样写:
Class<Number> genericNumberClass = int.class;//即使Integer继承自Number
- 泛型在使用子类类型得到超类类型时要使用模糊匹配:
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();
// 这样写编译不了,哪怕类型是对的:
// Class<Toy> up2 = ftClass.getSuperclass();
// 而且这种情况下,使用该方法会只能得到Object类型,和上面的得到具体类型不一样:
Object obj = up.newInstance();
}
}
- 用于Class引用的转型语法:cast()方法
//House是Building的子类
public class ClassCasts {
public static void main(String[] args) {
Building b = new House();
Class<House> houseType = House.class;
House h = houseType.cast(b);
h = (House)b; // 或者直接这样做也行.
}
}
泛型
- 泛型可以编译时检查参数,且不需要类型转换
public class Holder3<T> {
private T a;
public Holder3(T a) { this.a = a; }
public void set(T a) { this.a = a; }
public T get() { return a; }
public static void main(String[] args) {
Holder3<Automobile> h3 =
new Holder3<Automobile>(new Automobile());
Automobile a = h3.get(); // No cast needed
// h3.set("Not an Automobile"); // Error
// h3.set(1); // Error
}
}
- 如果泛型方法能做到,就不要用泛型类,静态方法无法访问泛型类的参数类型
- 可变参数与泛型的使用:
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);
}
}
- 在泛型代码内部,无法获得有关泛型参数类型的信息
public class ErasedTypeEquivalence {
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);
}
} /* Output:
true
*///
IO
- list()方法过滤器:
public class DirList2 {
//需要实现这个接口用于回调
public static FilenameFilter filter(final String regex) {
// Creation of anonymous inner class:
return new FilenameFilter() {
private Pattern pattern = Pattern.compile(regex);
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
}; // End of anonymous inner class
}
public static void main(String[] args) {
File path = new File(".");
String[] list;
if(args.length == 0)
list = path.list();
else
list = path.list(filter(args[0]));
Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
for(String dirItem : list)
System.out.println(dirItem);
}
}