第一篇:java习惯用法总结
在Java编程中,有些知识 并不能仅通过语言规范或者标准API文档就能学到的。在本文中,我会尽量收集一些最常用的习惯用法,特别是很难猜到的用法。(Joshua Bloch的《Effective Java》对这个话题给出了更详尽的论述,可以从这本书里学习更多的用法。)
我把本文的所有代码都放在公共场所里。你可以根据自己的喜好去复制和修改任意的代码片段,不需要任何的凭证。
目录
实现:
o equals()o hashCode()o compareTo()o clone() 应用:
o StringBuilder/StringBuffer o Random.nextInt(int)o Iterator.remove()o StringBuilder.reverse()o Thread/Runnable o try-finally 输入/输出:
o 从输入流里读取字节数据 o 从输入流里读取块数据 o 从文件里读取文本 o 向文件里写文本 预防性检测:
o 数值 o 对象 o 数组索引 o 数组区间 数组:
o 填充元素
o 复制一个范围内的数组元素 o 调整数组大小 包装
o 个字节包装成一个int o 分解成4个字节
实现equals()class Person { String name;intbirthYear;byte[] raw;publicboolean equals(Object obj){ if(!objinstanceof Person)return false;Person other =(Person)obj;returnname.equals(other.name)&&birthYear == other.birthYear &&Arrays.equals(raw, other.raw);} publicinthashCode(){...} } 参数必须是Object类型,不能是外围类。
foo.equals(null)必须返回false,不能抛NullPointerException。(注意,null instanceof 任意类 总是返回false,因此上面的代码可以运行。)
基本类型域(比如,int)的比较使用 ==,基本类型数组域的比较使用Arrays.equals()。覆盖equals()时,记得要相应地覆盖 hashCode(),与 equals()保持一致。参考: java.lang.Object.equals(Object)。
实现hashCode()class Person { String a;Object b;byte c;int[] d;publicinthashCode(){ returna.hashCode()+ b.hashCode()+ c + Arrays.hashCode(d);} publicboolean equals(Object o){...} } 当x和y两个对象具有x.equals(y)== true,你必须要确保x.hashCode()== y.hashCode()。根据逆反命题,如果x.hashCode()!= y.hashCode(),那么x.equals(y)== false 必定成立。 你不需要保证,当x.equals(y)== false时,x.hashCode()!= y.hashCode()。但是,如果你可以尽可能地使它成立的话,这会提高哈希表的性能。
hashCode()最简单的合法实现就是简单地return 0;虽然这个实现是正确的,但是这会导致HashMap这些数据结构运行得很慢。
参考:java.lang.Object.hashCode()。
实现compareTo()class Person implements Comparable
{ String firstName;String lastName;int birthdate;// Compare by firstName, break ties by lastName, finally break ties by birthdate publicintcompareTo(Person other){ if(firstName.compareTo(other.firstName)!= 0)returnfirstName.compareTo(other.firstName);else if(lastName.compareTo(other.lastName)!= 0)returnlastName.compareTo(other.lastName);else if(birthdate
只关心返回结果的正负号(负/零/正),它们的大小不重要。Comparator.compare()的实现与这个类似。参考:java.lang.Comparable。
实现clone()class Values implements Cloneable { String abc;double foo;int[] bars;Date hired;public Values clone(){ try { Values result =(Values)super.clone();result.bars = result.bars.clone();result.hired = result.hired.clone();return result;} catch(CloneNotSupportedException e){ // Impossible throw new AssertionError(e);} } } 使用 super.clone()让Object类负责创建新的对象。
基本类型域都已经被正确地复制了。同样,我们不需要去克隆String和BigInteger等不可变类型。 手动对所有的非基本类型域(对象和数组)进行深度复制(deep copy)。
实现了Cloneable的类,clone()方法永远不要抛CloneNotSupportedException。因此,需要捕获这个异常并忽略它,或者使用不受检异常(unchecked exception)包装它。
不使用Object.clone()方法而是手动地实现clone()方法是可以的也是合法的。参考:java.lang.Object.clone()、java.lang.Cloneable()。
使用StringBuilder或StringBuffer // join([“a”, “b”, “c”])-> “a and b and c” String join(List
使用StringBuilder或者StringBuffer时,可以使用append()方法添加文本和使用toString()方法去获取连接起来的整个文本。
优先使用StringBuilder,因为它更快。StringBuffer的所有方法都是同步的,而你通常不需要同步的方法。
参考java.lang.StringBuilder、java.lang.StringBuffer。
生成一个范围内的随机整数 Random rand = new Random();// Between 1 and 6, inclusive intdiceRoll(){ returnrand.nextInt(6)+ 1;} 总是使用Java API方法去生成一个整数范围内的随机数。
不要试图去使用 Math.abs(rand.nextInt())% n 这些不确定的用法,因为它的结果是有偏差的。此外,它的结果值有可能是负数,比如当rand.nextInt()== Integer.MIN_VALUE时就会如此。
参考:java.util.Random.nextInt(int)。
使用Iterator.remove()void filter(List
返转字符串
String reverse(String s){ return new StringBuilder(s).reverse().toString();} 这个方法可能应该加入Java标准库。
参考:java.lang.StringBuilder.reverse()。
启动一条线程
下面的三个例子使用了不同的方式完成了同样的事情。实现Runnnable的方式:
void startAThread0(){ new Thread(new MyRunnable()).start();} classMyRunnable implements Runnable { public void run(){...} } 继承Thread的方式:
void startAThread1(){ newMyThread().start();} classMyThread extends Thread { public void run(){...} } 匿名继承Thread的方式:
void startAThread2(){ new Thread(){ public void run(){...} }.start();} 不要直接调用run()方法。总是调用Thread.start()方法,这个方法会创建一条新的线程并使新建的线程调用run()。
参考:java.lang.Thread, java.lang.Runnable。
使用try-finally I/O流例子:
voidwriteStuff()throws IOException { OutputStream out = new FileOutputStream(...);try { out.write(...);} finally { out.close();} } 锁例子:
voiddoWithLock(Lock lock){ lock.acquire();try {...} finally { lock.release();} } 如果try之前的语句运行失败并且抛出异常,那么finally语句块就不会执行。但无论怎样,在这个例子里不用担心资源的释放。
如果try语句块里面的语句抛出异常,那么程序的运行就会跳到finally语句块里执行尽可能多的语句,然后跳出这个方法(除非这个方法还有另一个外围的finally语句块)。
从输入流里读取字节数据
InputStream in =(...);try { while(true){ int b = in.read();if(b ==-1)break;(...process b...)} } finally { in.close();} read()方法要么返回下一次从流里读取的字节数(0到255,包括0和255),要么在达到流的末端时返回-1。
参考:java.io.InputStream.read()。
从输入流里读取块数据
InputStream in =(...);try { byte[] buf = new byte[100];while(true){ int n = in.read(buf);if(n ==-1)break;(...process buf with offset=0 and length=n...)} } finally { in.close();} 要记住的是,read()方法不一定会填满整个buf,所以你必须在处理逻辑中考虑返回的长度。参考: java.io.InputStream.read(byte[])、java.io.InputStream.read(byte[], int, int)。
从文件里读取文本
BufferedReader in = new BufferedReader(newInputStreamReader(new FileInputStream(...), “UTF-8”));try { while(true){ String line = in.readLine();if(line == null)break;(...process line...)} } finally { in.close();} BufferedReader对象的创建显得很冗长。这是因为Java把字节和字符当成两个不同的概念来看待(这与C语言不同)。
你可以使用任何类型的InputStream来代替FileInputStream,比如socket。当达到流的末端时,BufferedReader.readLine()会返回null。要一次读取一个字符,使用Reader.read()方法。
你可以使用其他的字符编码而不使用UTF-8,但最好不要这样做。参考:java.io.BufferedReader、java.io.InputStreamReader。
向文件里写文本
PrintWriter out = new PrintWriter(newOutputStreamWriter(new FileOutputStream(...), “UTF-8”));try { out.print(“Hello ”);out.print(42);out.println(“ world!”);} finally { out.close();} Printwriter对象的创建显得很冗长。这是因为Java把字节和字符当成两个不同的概念来看待(这与C语言不同)。
就像System.out,你可以使用print()和println()打印多种类型的值。你可以使用其他的字符编码而不使用UTF-8,但最好不要这样做。参考:java.io.PrintWriter、java.io.OutputStreamWriter。
预防性检测(Defensive checking)数值
int factorial(int n){ if(n < 0)throw new IllegalArgumentException(“Undefined”);else if(n >= 13)throw new ArithmeticException(“Result overflow”);else if(n == 0)return 1;else return n * factorial(noff 填充数组元素 使用循环: // Fill each element of array 'a' with 123 byte[] a =(...);for(int i = 0;i Arrays.fill(a,(byte)123); 参考:java.util.Arrays.fill(T[], T)。 参考:java.util.Arrays.fill(T[], int, int, T)。 复制一个范围内的数组元素 使用循环: // Copy 8 elements from array 'a' starting at offset 3 // to array 'b' starting at offset 6, // assuming 'a' and 'b' are distinct arrays byte[] a =(...);byte[] b =(...);for(int i = 0;i < 8;i++)b[6 + i] = a[3 + i];(优先)使用标准库的方法: System.arraycopy(a, 3, b, 6, 8); 参考:java.lang.System.arraycopy(Object, int, Object, int, int)。 调整数组大小 使用循环(扩大规模): // Make array 'a' larger to newLen byte[] a =(...);byte[] b = new byte[newLen];for(int i = 0;i // Make array 'a' smaller to newLen byte[] a =(...);byte[] b = new byte[newLen];for(int i = 0;i a = Arrays.copyOf(a, newLen); 参考:java.util.Arrays.copyOf(T[], int)。 参考:java.util.Arrays.copyOfRange(T[], int, int)。把4个字节包装(packing)成一个int intpackBigEndian(byte[] b){ return(b[0] & 0xFF)<< 24 |(b[1] & 0xFF)<< 16 |(b[2] & 0xFF)<< 8 |(b[3] & 0xFF)<< 0;} intpackLittleEndian(byte[] b){ return(b[0] & 0xFF)<< 0 |(b[1] & 0xFF)<< 8 |(b[2] & 0xFF)<< 16 |(b[3] & 0xFF)<< 24;} 把int分解(Unpacking)成4个字节 byte[] unpackBigEndian(int x){ return new byte[] {(byte)(x>>> 24),(byte)(x>>> 16),(byte)(x>>> 8),(byte)(x>>> 0)};} byte[] unpackLittleEndian(int x){ return new byte[] {(byte)(x>>> 0),(byte)(x>>> 8),(byte)(x>>> 16),(byte)(x>>> 24)};} 总是使用无符号右移操作符(>>>)对位进行包装(packing),不要使用算术右移操作符(>>)。 在这忙忙碌碌的这段时间里,经过老师的辅导,迅速的将一点没有学的JAVA基础搞定了!有了基础学习还是好,万事开头难这句话说的太对了,学计算机语言我觉得记忆好的方法就是多打代码,课前预习,课堂上认真听讲,把现学的方法把以前所做的作业用最简便的方法再一次巩固,创新最重要,在后续的学习中,得要加倍努力学习。 其实学java有不懂,要先自己思考。想清楚这句代码是什么意思。为什么要写在这,等等之类的。等你真的搞不明白的时候,就一定要向老师咨询,不要感到有什么丢人的。因为不会是很正常的事。并不是每个人都是天才,一学就会,一学就能运用自如的。学java有一点是非常重要的,就是练习。一段代码要不停的敲,多敲几遍,尤其是自己不熟悉或者不熟练的代码,更要敲。不要感觉到厌烦,其实我感觉敲代码挺好玩的,并不是一件很枯燥的事。 老师平常布置的课后上机练习题一定要做,课后的练习题能够让你把新学到的知识巩固一遍,能够加深记忆,不会让你在以后做题的时候感到没一点思路。 当感觉到不会的时候,千万不要气馁,因为这很正常,现在的学习就是为了培养你有一个逻辑思维,为了以后开发软件的时候有个完整,清晰的思路。 其实,总体来说。学习java很快乐。尤其是当你遇到一道自己不会的题,然后,又通过自己的努力解决了,那时候,那种心情不是用言语来表达的。就好像你遇到一个数学难题,自己解决了之后那种成就感一样。 学java的时候一定要,放松心情,轻轻松松的来学,随时让自己快乐着,这样能够让你能够更快的接受java,千万不要有什么心理负担,因为java的特点之一就是--简单易懂。只要自己努力到了,就一定能够学好java。 学完了JAVA今天我们用项目案例:迷你DVD管理器来巩固了我们所学的所有内容,通过这项目的操练,首先,1、项目用到了会使用顺序、分支、循环、跳转语句编写程序,2、要会使用数组、操作字符串,3、会使用带参的方法; 4、会定义类、创建和使用对象,看到这些脑袋里一片迷茫啊!不知道怎样写,然后想到早写晚写都一样,就照着书上写起来了,到现在还是弄不懂的就是那个对象数组,不知道怎样去理解,抽象的把我抽晕了,有望老师来给我们补补这一章,在实现DVD的业务处理时,计算时差还是不懂,照着书上打了一遍,可还是得不到想要的结果,经过网上的搜寻与老师讲解,现在已略懂一二了,在做完这项目后,真不知道当时是怎样敲出来的,难道这就是所说的灵感!感觉很高兴,现在已习惯了代码报错,其实代码报错是一件值得鼓励的事,因为没有错就觉得自己什么都懂了,在学习中相信每一个人都遇到过挫折吧!但一定要想方法战胜挫折!我的战胜挫折方法就是不懂思考后还不懂就问,懂了以后就笔记本记下当时的解决方案!学习刚开始!后面的路很长,慢慢的去磨炼了!总结完毕! Java定义格式: 1、数据类型的强制性转换语法: (欲转换的数据类型)变量名称; 2、if语句的格式: if(条件判断) 语句; 3、if语句的格式: if(条件判断){ 语句1; 语句2; … 语句3; } 4、if…else语句的格式: if(判断条件){ 语句主体1; } else { 语句主体2; } 5、条件语句格式: 条件判断?表达式1:表达式2; 即: If(条件判断){ 变量 x=表达式1; } Else { 变量x=表达式2; } 6、if…else if…else语句格式: if(判断条件1){ 语句主体1; } else if(判断条件2){ 语句主体2; } … //多个else if语句 Else { 语句主体3; } 7、switch语句格式: switch(表达式){ case 选择值1:语句主体1; break; case 选择值2:语句主体2; break; …… case 选择值n:语句主体n; break; default: 语句主体; } 8、while循环语句格式: while(判断条件){ 语句1; 语句2; …… 语句n; } 9、do……while语句格式: do { 语句1; 语句2; …… 语句n; }while(判断条件) 10、for循环语句格式: for(赋值初值:判断条件:赋值增减量){ 语句1; …… 语句n; } 11、break语句格式: for(赋值初值:判断条件:赋值增减量){ 语句1;12、13、14、15、16、17、18、19、20、21、语句2; …….break; …… //若执行break语句,此块内的语句都不再执行 语句n; } continue语句格式: for(赋值初值:判断条件:赋值增减量){ 语句1; 语句2; …… Continue;//若执行continue语句,此块内的语句不再执行 …… 语句n; } 一维数组的声明与分配内存格式: 数据类型 数组名[]; //声明一维数组 数组名=new 数据类型[个数]; //分配内存给数组 声明数组的同时分配内存格式: 数据类型 数组名[]=new 数据类型[个数] 数组长度的取得格式: 数组名.length 数组初值的赋值格式: 数据类型 数组名[]={初值0,初值1,…,初值n}; 二维数组的声明格式: 数据类型 数组名[][]; 数据名=new 数据类型[行的个数][列的个数]; 二维数组色声明及分配内存格式: 数据类型 数据名[][]=new 数据类型[行的个数][列的个数]; 二维数组初值的赋值格式: 数据类型 数组名={{第0行初值},{第1行初值},…… {第n行初值} }; 取得二维数组的行数与特定行的元素的个数格式: 数组名.length //取得数组的行数 数组名[行的索引].length //取得特定行元素的个数 声明方法并定义内容格式: 返回值类型 方法名称(类型 参数1,类型 参数2,…){ 程序语句; return 表达式;22、23、24、25、26、27、28、} 类的定义格式: class 类名称 { 数据类型 属性; …… 返回值的数据类型 方法名称(参数1,参数2…){ 程序语句; return 表达式; } } 对象的产生格式: 类名 对象名=new 类名(); 访问对象中某个变量或方法格式: 访问对象:对象名称.属性名 访问方法:对象名称.方法名()封装类中的属性或方法格式: 封装属性:private 属性类型 属性名 封装方法:private 方法返回类型 方法名称(参数)构造方法的定义格式: class 类名称 { 访问权限 类名称(类型1 参数1,类型2 参数2,…){ 程序语句; … //构造方法没有返回值 } } 定义内部类格式: 标识符 class 外部类名称 { //外部类的成员 标识符 class 内部类的名称 { //内部类的成员 } } 类的继承格式 class父类 //定义父类 { } class 子类 extends 父类 //用extends关键字实现类的继承 {29、30、31、32、33、34、35、} Super调用父类中的属性或方法格式: super.父类中的属性; super.父类中的方法(); 子类复写父类中的方法的格式: class Super { 访问权限 方法返回值类型 方法1(参数1) { } } class Sub extends Super { 访问权限 方法返回值类型 方法1(参数1)//复写父类中的方法 { } } 抽象类的定义格式: abstract class 类名称 //定义抽象类 { 声明数据成员; 访问权限 返回值的数据类型 方法名称(参数…) { } abstract 返回值的数据类型 方法名称(参数…); //定义抽象方法,在抽象方法里,没有定义处理的方式 } 接口的定义格式: Interface 接口名称 //定义抽象类 { final 数据类型 成员名称=常量; //数据成员必须赋初值 abstract 返回值的数据类型 方法名称(参数…); //抽象方法,注意在抽象方法里,没有定义处理的方式 } 接口的实现格式: class 类名称 implements 接口A,接口B //接口的实现 { …… } 接口的扩展格式: Interface 子接口名称 extends 父接口1,父接口2,… { …… } 异常处理的语法格式: try36、37、38、39、40、41、{ 要检查的程序语句; … } catch(异常类 对象名称){ 异常发生时的处理语句; } finally { 一定会运行到的程序代码; } 抛出异常的语法格式: Throw 异常类实例对象; 由方法抛出异常格式: 方法名称(参数…)throws 异常类1,异常类2,… 编写自定义异常类格式: class 异常名称 extends Exception { …… } Package的声明格式: Package package名称; package的导入格式: import package名称.类名称; 由键盘输入数据基本形式格式: Import java.io.*;Public class class_name //类名 { Public static void main(String args[])throws IOException { BufferedReader buf;//声明buf为BufferedReader类的变量 String str;//声明str为String类型的变量 …… Buf=new BufferedReader(new InputStreamReader(System.in));//产生buf对象 Str=buf.readLine(); //读入字符串至buf …… } } Java实验 1.调试HelloWorld程序 2.this,super,get ,set,把课本90页程序4.7中的name改成私有变量 3.继承,重写,父类引用指向子类对象 4.验证数组Arrays类和Collection类 5.编写一个自己的异常类并捕获之。 6.编写一个类,将该类的几个对象装入TreeSet容器中,并将该容器的内容通过输出流写入文件中。 前三章重点 0.java的数据类型:四类八种-(1)布尔类型Boolean;(2)字符类型char;(3)整数byte,short,int,long;(4)浮点类型:float,double;1.面向对象的3个基本特征:封装,继承,多态。 2.构造方法和普通方法的区别:对构造方法而言,它有以下特性---(1)方法名必须与要创建对象的类名相同。(2)不允许声明返回类型,即使声明为void也不被允许。 3.this关键字:是一个引用,this引用指向的是其本身所在方法的当前对象。this的使用方法:(1)调用成员变量;(2)可以用this()调用其他构造函数。 4.java中只对类成员变量进行自动初始化,而方法内部的局部变量在使用前必须手动初始化。 5.static 关键字:可用来修饰类的成员变量和成员方法,需要注意两点--(1)静态方法不能调用类的非静态方法,不能访问类的非静态变量。(2)静态方法和静态变量(非私有的)可以有两种调用方式,一是实例对象调用,二是类名直接调用。 6.类成员访问控制修饰符public、private、default(可不写,即缺省状态)、protected的使用:public-公用的;private-私有的,只在定义它的类内部使用;default-可以被同一包中的类访问;protected-既可以被同一包中的类访问,也可以被不在同一包中的子类访问。 7.方法的重载:指方法名相同,而方法的参数列表不相同。参数列表不同有三层意思:(1)参数类型不同。(2)参数顺序不同。(3)参数个数不同。另外需注意,在同一个类中,当方法名和参数列表都相同时,访问控制修饰符或方法返回类型不相同并不是方法的重载,而且这种情况在java中是不被允许的。 第四五章重点 1.继承:需使用关键字extends.在使用继承时需注意--(1)每个子类只能定义一个超类(父类),即extends后面应且仅应跟一个类名作为该类的父类。(2)父类中的私有属性和私有方法不能被继承。 2.方法的重写:即子类对超类中的方法保持方法名、返回类型和参数列表不变,重写了方法体,使子类和超类完成不同的工作。重写需注意下面几个关键点:(1)超类中的私有方法不能被重写。(2)访问限制符强度由低到高依次是:public、protected、default、private,在重写过程中,如果子类和父类中方法的返回值、方法名及方法的参数列表都相同,这时,要求子类中该方法的访问限制符强度不能超过父类的。即如果父类中为public时,子类也只能为public,而不能是余下的三种。 3.重载(overload)和覆盖(override)的区别:(1)重载—发生在一个类的内部或子类与父类之间,要求方法名相同而参数列表不一样。(2)覆盖—只能发生在继承过程中,要求子类方法的返回类型,方法名和参数列表同父类的都相同,而方法体不一样。 4.构造器的调用顺序:先祖先,再客人,最后自己。 5.多态:指在类继承中子类和父类中可以有同名但意义或实现方式不同的属性和方法。分为:覆盖和重载。多态的优点:因为多态,可以在程序中对类进行扩展,而不需改变那些操作基类接口的方法。 6.动态绑定:指在代码执行期间,判断所引用对象的实际类型,根据其实际类型调用相应方法。动态绑定存在的三个必要条件--(1)要有继承;(2)要有重写(覆盖);(3)父类引用指向子类对象(向上转型)。 7.Object中常用的方法总结:toString();wait();equals();notify();notifyAll();hashCode();getClass();clone();finalize();(呵呵,尽可能记几个,以防老师让咱们列举)注:java中Object类是所有类的父类,即java中所有的类都有上述9种方法。 8.对象的比较:注意关键字instanceof的使用。 9.抽象类: 抽象方法—用关键字abstract修饰的方法,该方法只需方法的声明,而不需方法的实现(即无方法体)。 抽象类——至少包含一个抽象方法的类,也用abstract关键字声明。(注:(1)抽象类中可以有一些具体方法。(2)抽象类不能实例化。(3)子类继承抽象类必须实现其抽象方法。) 10.接口: (1)可以看成是高度抽象的抽象类,但是接口不是类。 (2)用关键字interface来声明接口,用关键字imlpements来实现接口。 (3)接口不能有具体方法,不能有实例数据,但可以定义常量。 (4)实现接口的非抽象类必须实现接口的所有方法。 (5)每个类可以实现多个接口,这些接口用逗号隔开,同时,一个接口可以被多个类实现。 第六章:重点看一下实验四 1.容器——Collection(接口)和Map(接口).Collection——Set(接口)和List(接口)。其中,List必须保持元素的特定顺序,常见的实现类有ArrayList和LinkedList;Set不能有重复元素,常见的实现类有HashSet和TreeSet。 Map——一组成对的“键值对”对象,即其元素是成对的对象,常见的实现类有HashMap和TreeMap。 第七章 1.异常类的根类是Throwable类,它的两个直接子类是Error类和Exception类。 2.异常中常用的5个关键字为:try,catch,finally,throw,throws.其中,try和catch:用于捕获异常;finally:无论try块中的异常是否抛出,finally中的代码块总能被执行;throw:抛出异常;throws:声明异常。 3.“未被检查的异常(Unchecked Exceptions)”和“受检查的异常(Checked Exceptions)”—— Unchecked Exceptions :编译器不检查方法是否处理或抛出的异常,即不做处理,编译时不报错。 Checked Exceptions:受编译器检查的异常,即不做处理编译时通不过。 4.常见的几种Checked Exceptions:ClassNotFoundExceptionIOExceptionInterruptedExceptionFileNotFoundException.(尽可能的记几个吧,以防不测)第八章 1.流--字节流和字符流; 流--节点流和处理流。 2.所有的输入流都是从抽象类InputStream和Reader继承而来。所有输出流都是从抽象类OutputStream和Writer继承而来。3.字节流:InputStream和OutputStream;字符流:Reader和Writer; 4.节点流:直接与文件等底层打交道,如FileInputStreamFileOutputStreamFileReaderFileWriter.处理流:相当于包装流,套在节点流上,方便数据处理。相关一些用法,具体参考最后一次实验。 调用父类构造方法 在子类的构造方法中可使用super(argument_list)语句调用父类的构造方法 如果子类的构造方法中没有显示地调用父类构造方法,也没有使用this关键字调用重载的其它构造方法,则系统默认调用父类无参数的构造方法 如果子类构造方法中既未显式调用父类构造方法,而父类中又没有无参的构造方法,则编译出错 1public class Person { 3private String name; 4private int age;private Date birthDate; 7public Person(String name, int age, Date d){ 8this.name = name; 9this.age = age; 10this.birthDate = d; 11} 12public Person(String name, int age){ 13this(name, age, null); 14} 15public Person(String name, Date d){ 16this(name, 30, d); 17} 18public Person(String name){ 19this(name, 30);} 21// …… 22} 1public class Student extends Person { 2private String school; 4public Student(String name, int age, String s){ 5super(name, age); 6school = s; 7} 8public Student(String name, String s){ 9super(name); 10school = s; 11} 12public Student(String s){ // 编译出错: no super()13school = s; 14} 15} 对象构造和初始化细节 分配存储空间并进行默认的初始化 按下述步骤初始化实例变量 1.绑定构造方法参数 2.如有this()调用,则调用相应的重载构造方法,然后跳转到步骤5 3.显式或隐式追溯调用父类的构造方法(Object类除外) 4.进行实例变量的显式初始化操作 5.执行当前构造方法的方法体 ==操作符与equals方法 ==操作符与equals方法的区别: 引用类型比较引用;基本类型比较值; equals()方法只能比较引用类型,“==”可以比较引用类型及基本类型; 特例:当用equals()方法进行比较时,对类File、String、Date及封装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个实例; 用“==”进行比较时,符号两边的数据类型必须一致(可自动转换的基本数据类型除外),否则编译出错; 由装箱引发的——Integer比较的来龙去脉 前置知识: 众所周之,java是保留了int,char等基本数据类型的,也就是说int类型的并不是对象,然而有些方法却需要object 类型的变量,所以java使用了装箱机制,我们可一自豪的这样声明一个整型变量:Integer a = new Integer(10);那么整型的a也就是对象了,那这句是什么意思呢:Integer a= 10;java中可以这样声明一个对象吗?当然不是,从jdk1.5后,java实现了自动装箱,也就是自动将Integer a =10 中的int类型的10转化为了 Integer类型。好,有了前面的只是我们且先看一个题目: Integer a = 127; Integer b = 127; Integer c = 128; Integer d = 128; System.out.println(a==b); System.out.println(c==d); 答案是什么呢? 如果您回答true,false,那么很遗憾的告诉你,哈哈,其实你答对了!! 那我们晕了就相差1的两个数为啥走向了“反目成仇”的地步呢?凭啥127等于127,我128就不等于128呢?且听我慢慢道来,Integer a =127,Integer a=128。 127,128应该不会造成什么差异吧,难道是自动装箱的过程有猫腻?找下源码看看: private static class IntegerCache { private IntegerCache(){} static final Integer cache[] = new Integer[-(-128)+ 127 + 1];static { for(int i = 0;i < cache.length;i++) cache[i] = new Integer(i128); } 这是用一个for循环对数组cache赋值,cache[255] = new Integer(255-128),也就是newl一个Integer(127),并把引用赋值给cache[255],好了,然后是Integer b= 127,流程基本一样,最后又到了cache[255] = new Integer(255-128),这一句,那我们迷糊了,这不是又new了一个对象127吗,然后把引用赋值给cache[255],我们比较这两个引用(前面声明a的时候也有一个),由于是不同的地址,所以肯定不会相等,应该返回false啊!呵呵,这么想你就错了,请注意看for语句给cache[i]初始化的时候外面还一个{}呢,{}前面一个大大的static关键字大咧咧的杵在哪呢,对静态的,那么我们就可以回想下static有什么特性了,只能初始化一次,在对象间共享,也就是不同的对象共享同一个static数据,那么当我们Integer b = 127的时候,并没有new出一个新对象 来,而是共享了a这个对象的引用,记住,他们共享了同一个引用!!,那么我们进行比较a==b时,由于是同一个对象的引用(她们在堆中的地址相同),那当然返回true了!! 然后我们在看Integer c = 128;Integer d = 128;这两句。现在不用我说就应该能明白了吧,当数据不再-128到127之间时,是不执行return IntegerCache.cache[i + offset];这句的,也就是不会返回一个static的引用,而是执行了return new Integer(i);于是当 Integer d = 128 时,又会重新返回一个引用,两个不同的引用 在做c==d 的比较时当然返回false了! 下面附上本程序的字节码以供喜欢底层的读者参考: Compiled from “CompareInteger.java” public class CompareInteger extends java.lang.Object{ public CompareInteger(); Code: 0:aload_0 1:invokespecial#1;//Method java/lang/Object.“ public static void main(java.lang.String[]); Code: 0:bipush 127 2:invokestatic#2;//Method java/lang/Integer.valueOf:(I)Ljava/lang/Int eger; 5:astore_1 6:bipush 127 8:invokestatic#2;//Method java/lang/Integer.valueOf:(I)Ljava/lang/Int eger; 11: astore_2 12: sipush 128 15: invokestatic#2;//Method java/lang/Integer.valueOf:(I)Ljava/lang/Int eger; 18: astore_3 19: sipush 128 22: invokestatic#2;//Method java/lang/Integer.valueOf:(I)Ljava/lang/Int eger; 25: astore 4 27: getstatic#3;//Field java/lang/System.out:Ljava/io/PrintStream; 30: aload_1 31: aload_2 32: if_acmpne39 35: iconst_1 36: goto40 39: iconst_0 40: invokevirtual#4;//Method java/io/PrintStream.println:(Z)V43: getstatic#3;//Field java/lang/System.out:Ljava/io/PrintStream; 46: aload_3 47: aload4 49: if_acmpne56 52: iconst_1 53: goto57 56: iconst_0 57: invokevirtual#4;//Method java/io/PrintStream.println:(Z)V60: return } 评论:呵呵,这么想你就错了,请注意看for语句给cache[i]初始化的时候外面还一个{}呢,{}前面一个大大的static关键字大咧咧的杵在哪呢,对静态的,那么我们就可以回想下static有什么特性了,只能初始化一次,在对象间共享,也就是不同的对象共享同一个static数据,那么当我们Integer b = 127的时候,并没有new出一个新对象来,而是共享了a这个对象的引用,记住,他们共享了同一个引用!! 呵呵,博主我被你这句话小小的误导了一下,其实你这里说的原理没错,但是把位置说错了,这段代码只是初始化cache: static { for(int i = 0;i < cache.length;i++) cache[i] = new Integer(i-128); } 但真正让cache[i]为static变量的是这句代码: static final Integer cache[] = new Integer[-(-128)+ 127 + 1];第二篇:JAVA总结专题
第三篇:Java格式总结
第四篇:Java总结
第五篇:java总结