Java编程思想学习笔记

来源:互联网 发布:安卓锁机软件生成器 编辑:程序博客网 时间:2024/05/25 21:35
多态
后期绑定:向对象发送消息时,被调用的代码直到运行时才能确定。编译器确保被调用方法的存在,并对调用参数和返回值执行类型检查,但并不知道将要执行的确切代码。
为了执行后期绑定,Java使用一小段特殊的代码来替代绝对地址调用。这段代码使用在对象中存储的信息来计算方法体的地址。
向上转型:将导出类看做是它的基类


单根继承结构
Object
单根继承体系保证所有对象都拥有某些功能。在整个系统里,你因此知道可以在每个对象身上执行某些基本操作。单根继承体系再加上<在heap之中产生所有对象>,大大的简化了参数传递动作(这也是C++里十分复杂的问题之一)。
单根继承体系也使垃圾回收器的实现更加容易。所有必备功能都可以安置于基类本身,然后垃圾回收器便可以发送适当消息给系统中的每个对象。如果缺乏<单根继承体系>及<完全透过引用来操作对象>的系统特性,垃圾回收器的实现就会十分困难。
由于所有对象都保证具有其类型信息,所以你必不会因为无法判断对象的确切类型而陷入动弹不得的僵局。对于异常处理之类的系统级操作行为而言,这一点格外重要,并且也能为程序设计带来更加优秀的弹性。


容器
任何时候都可以动态扩充。
JavaSE5的重大变化之一就是增加了参数化类型,即范型。通过范型,容器不再只是存储Object,而可以作用于特定类型。
常用集合类(容器类)的继承结构如下: 
Collection<--List<--Vector 
Collection<--List<--ArrayList 
Collection<--List<--LinkedList 
Collection<--Set<--HashSet 
Collection<--Set<--HashSet<--LinkedHashSet 
Collection<--Set<--SortedSet<--TreeSet 
Map<--SortedMap<--TreeMap 
Map<--HashMap 


用引用操纵对象
尽管一切都看做对象,但操纵的标识符实际上是对象的一个引用。
特例:基本类型
不用new,而是创建一个并非是引用的“自动”变量,这个变量直接存储“值”,并置于堆栈中,因此更加高效。


对象的作用域
当用new创建一个Java对象时,它可以存活于作用域之外。引用s在作用域终点就消失了,然而s指向的String对象仍然占据内存空间。(GC)


参数传递
不管Java参数的类型是什么,一律传递参数的副本。对此,thinking in Java一书给出的经典解释是When you’re passing primitives into a method, you get a distinct copy of the primitive. When you’re passing a reference into a method, you get a copy of the reference.(如果Java是传值,那么传递的是值的副本;如果Java是传引用,那么传递的是引用的副本。)
  在Java中,变量分为以下两类:
  ① 对于基本类型变量(int、long、double、float、byte、boolean、char),Java是传值的副本。(这里Java和C++相同)
  ② 对于一切对象型变量,Java都是传引用的副本。其实传引用副本的实质就是复制指向地址的指针,只不过Java不像C++中有显著的*和&符号。(这里Java和C++不同,在C++中,当参数是引用类型时,传递的是真实引用而不是引用副本)
  需要注意的是:String类型也是对象型变量,所以它必然是传引用副本。不要因为String在Java里面非常易于使用,而且不需要new,就被蒙蔽而把String当做基本变量类型。只不过String是一个非可变类,使得其传值还是传引用显得没什么区别。
  对基本类型而言,传值就是把自己复制一份传递,即使自己的副本变了,自己也不变。而对于对象类型而言,它传的引用副本(类似于C++中的指针)指向自己的地址,而不是自己实际值的副本。


有一个特定类会自动被导入每一个Java文件:java.lang


Java没有sizeof
在C/C++中,需要使用sizeof()的最大原因是为了移植。不同数据类型在不同机器上可能有不同大小。Java不需要,因为所有数据类型在所有机器中的大小都是相同的。


Foreach语法
JavaSE5引入了一种新的更加简洁的for语法用于数组和容器,表示不必创建int变量去对由访问项构成的序列进行计数。
任何返回一个数组的方法都可以使用foreach,例如
for (char c : "An African Swallow".toCharArray()) 
foreach还可以用于任何Iterable对象


标签
语法: lable:
在Java中,标签起作用的唯一一个地方是在迭代语句之前。在标签和迭代语句之间不能有任何语句。在迭代之前设置标签的唯一理由是:我们希望在其中嵌套另一个迭代或者一个开关。这是由于break和continue通常只中断当前循环,但若随标签一起使用,它们就会中断循环,跳到标签所在的地方。


构造器
名称与类名相同。
构造器是一种特殊类型的方法,因为它没有返回值。
如果已经定义了一个构造器,编译器就不会帮你自动创建默认构造器。


this关键字
Banana a = new Banana();
a.peel(1);
编译器在内部把“所操纵对象的引用”作为第一个参数传递给peel()。即Banana.peel(a,1);
假设希望在方法内部获得对当前对象的引用,由于这个引用是由编译器“偷偷”传入的,所以没有标识符可用。但为此有个专门的关键字this。
this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。
例如,当需要返回对当前对象的引用时,写为 return this;
this关键字对于将当前对象传递给其他方法也很有用。


垃圾回收
思想:对任何“活”的对象,一定能最终追溯到其存活在堆栈或静态存储区之中的引用。由此,从堆栈和静态存储区开始,遍历所有引用...


对象的创建
总结一下对象的创建过程,假设有个名为Dog的类:
1.即使没有显式地使用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog的对象时(构造器可以看成是静态方法),或者Dog类的静态方法或静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。
2.然后载入Dog.class(后面会学到,这将创建一个Class对象(确实很重要)),有关静态初始化的所有动作都会执行。因此,静态初始化只有在Class对象首次加载的时候进行一次。
3.当用new Dog()创建对象的时候,首先在堆上为Dog对象分配足够的存储空间。
4.这块存储空间会被清零,这就自动地将Dog对象中的所有基本类型数据都设置为默认值(对数字来说就是0,对布尔型和字符型也相同),而引用则被设置成null。
5.执行所有出现于字段定义处的初始化动作。
6.执行构造器。


枚举类型
enum关键字
public enum Spiciness {
  NOT, MILD, MEDIUM, HOT, FLAMING
}
使用:
1. Spiciness howHot = Spiciness.MEDIUM;
2. for (Spiciness s : Spiciness.values())
enum可以在switch语句内使用。 


代码组织
Java可运行程序是一组可以打包并压缩为一个Java文档文件(JAR)的.class文件。
java解释器的运行过程:首先,找出环境变量CLASSPATH。CLASSPATH包含一个或多个目录,用作查找.class文件的根目录。从根目录开始,解释器获取包的名称并将每个句点替换成反斜杠,以从CLASSPATH根中产生一个路径名称,得到的路径会与CLASSPATH中的各个不同的项连接,解释器就在这些目录中查找与你要创建的类名称相关的.class文件。


toString()
每一个非基本类型的对象都有一个toString()方法,而且当编译器需要一个String而你却只有一个对象时,该方法便会被调用.
如果没有显式定义,则会使用Object默认的toString方法,该方法将打印类名,后面跟随该对象的散列码的无符号十六进制表示。


super
Java用super关键字表示超类的意思,比如用于调用从基类继承而来的方法。


初始化基类
Java会自动在导出类的构造器中插入对于基类构造器的调用。构造过程是从基类“向外”扩散的。
但是,如果没有默认的基类构造器,或者想调用一个带参数的基类构造器,就必须用关键字super显式地编写调用基类构造器的语句。例如super(i);


@Override
Java SE5新增加了@Override注解,当你想要覆写某个方法时,可以选择添加这个注解,在你不留心重载而非覆写了该方法时,编译器会报错。


final
当对对象的引用不是基本类型时运用final,含义不同,请注意:
1. 对于基本类型,final使数值恒定不变;
2. 用于对象引用,final使引用恒定不变,一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象,然而对象其自身是可以被修改的,Java并没有提供使任何对象恒定不变的途径(但可以自己编写类以取得使对象恒定不变的效果)。


初始化及类的加载
Java中,每个类的编译代码都存在于它自己的独立的文件中。该文件只在需要使用程序代码时才会被加载。一般来说,可以说:“类的代码在初次使用时才加载”。这通常指加载发生于创建类的第一个对象之时,但是当访问static域或方法时,也会发生加载。(构造器也是static方法,尽管static关键字并没有显式地写出来。因此更准确的讲,类是在其任何static成员被访问时加载的。)
---------------------
对Beetle 运行Java 时,发生的第一件事情是试图访问Beetle.main()(一个static方法),于是装载程序到外面找到那个类(Beetle.class)。
在装载过程中,装载程序注意它有一个基础类(即extends 关键字要表达的意思),所以随之将其载入。
无论是否准备生成那个基础类的一个对象,这个过程都会发生(请试着将对象的创建代码当作注释标注出来,自己去证实)。
若基础类含有另一个基础类,则另一个基础类随即也会载入,以此类推。
接下来,会在根基础类(此时是Insect)执行static 初始化,再在下一个衍生类执行,以此类推。
保证这个顺序是非常关键的,因为衍生类的初始化可能要依赖于对基础类成员的正确初始化。
此时,必要的类已全部装载完毕,对象就可以被创建了。
首先,这个对象中的所有基本数据类型都会设成它们的默认值,而将对象引用设为null(这是通过将对象内存设为二进制零值而一举生成的)。
随后会调用基础类构建器。在这种情况下,调用是自动进行的。但也完全可以用super 来自行指定构建器调用(就象在Beetle()构建器中的第一个操作一样)。
基础类的构建采用与衍生类构建器完全相同的处理过程。基础顺构建器完成以后,实例变量会按本来的顺序得以初始化。
最后,执行构建器剩余的主体部分。


抽象方法和抽象类
抽象方法:仅有声明而没有方法体
abstract void f();
包含抽象方法的类叫做抽象类,如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。


接口
接口中的方法必须是public的。即使不显式声明为public,但它们自动就是public的。
接口用于实现Java中的“伪多重继承”:如果要从一个非接口的类继承,那么只能从一个类去继承。其余的基元素都必须是接口,需要将所有接口名都置于implements关键字之后,用逗号将它们一一隔开。
接口中的域都自动是public,static和final的。


内部类
使用内部类最吸引人的原因是:每个内部类都能独立的继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
从这个角度看,内部类使得多重继承的解决方案变得完整。
也就是说,内部类允许继承多个非接口类型。


。。。。。。。。。。。。。。。


异常参数
所有标准异常类都有两个构造器,一个是默认构造器,另一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器。


捕获异常
try {
  ...
} catch (Type1 id1) {
  ...
} catch (Type2 id2) {
  ...
} ...


e.printStackTrace()
在Throwable类声明(Exception从此类继承),打印“从方法调用处直到异常抛出处”的方法调用序列。
信息将被输出到标准错误流。


异常与记录日志
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());
  }
}


异常说明
使用关键字throws,后面接一个所有潜在异常类型的列表。这种在编译时被强制检查的异常称为被检查的异常。
但从RuntimeException继承的异常,可以在没有异常说明的情况下被抛出。


重新抛出异常
如果只是把当前异常对象重新抛出,那个printStackTrace()方法显示的将是原来异常抛出点的调用栈信息。要想更新这个信息,可以调用fillInStackTrace()方法,这将返回一个Throwable对象,它是通过把当前调用栈信息填入原来那个异常对象而建立的。
异常链
常常会想要在捕获一个异常后抛出另一个异常,并且希望把原异常的信息保存下来,这被称为异常链。
通过把原始异常传递给新的异常作为cause来实现,使用initCause()方法。


Java标准异常
Thorwable: Error(编译时和系统错误), Exception(可被抛出的基本类型)
Exception: IOException(如FileNotFoundException), ClassNotFoundException, RuntimeException(不受检查异常,比如NullPointerException, IndexOutOfBoundsException)


finally
无论异常是否被抛出,finally子句总能被执行。
用途:当要把除内存之外的资源恢复到它们的初始状态时,就要用到。例如已经打开的文件或网络连接,在屏幕上画的图形。
在编写构造器时要格外细心,因为finally会每次都执行清理代码,如果构造器在其执行过程中半途而废,也许该对象的某些部分还没有被成功创建,而这些部分在finally子句中却是要被清理的。
对于在构造阶段可能会抛出异常,并且要求清理的类,最安全的方法是使用嵌套的try子句。
其基本规则是,在创建需要清理的对象之后,立即进入一个try-finally语句块。




不可变String
String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容。
不可变性会带来一定的效率问题,比如重载的“+”操作符。
使用StringBuilder(效率更高)/StringBuffer(线程安全)


正则表达式
String类自带了一个非常有用的正则表达式工具——split()方法。
Pattern和Matcher
Pattern p = Pattern.compile(arg);
Matcher m = p.matcher(args[0]);
while (m.find()) {
  ...
}


Scanner
Java SE5新增了Scanner类,它可以大大减轻扫描输入的工作负担。
其构造器可以接受包括File, InputStream, String或Readable对象。
所有的输入、分词以及翻译的操作都隐藏在不同类型的next方法中。
默认情况下,Scanner根据空白字符对输入进行分词。也可以用useDelimiter()来设置定界符。


Class对象
Class.forName()


RTTI
注册工厂
instanceof
反射
动态代理


泛型。。。


I/O流的典型使用方式
-缓冲输入文件:
public static String read(String filename) throws IOException {
  BufferedReader in = new BufferedReader(new FileReader(filename));
  String s;
  StringBuilder sb = new StringBuilder();
  while ((s - in.readLine()) != null)
    sb.append(s + "\n");
  in.close();
  return sb.toString();
}

0 0
原创粉丝点击