Java学习过程中的收获
1. String <--> Date
这种转换要用到java.text.SimpleDateFormat类 字符串转换成日期类型: 方法1: 也是最简单的方法 Date date=new Date("2008-04-14"); 方法2: SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");//小写的mm表示的是分钟 String dstr="2008-4-24"; java.util.Date date=sdf.parse(dstr); 日期转换成字符串: SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); java.util.Date date=new java.util.Date(); String str=sdf.format(date);
2. Iterable & Iterator
Iterator是迭代器类 Iterable是接口, 实现了Iterable就可以调用Iterator的方法 进而使用next hasNext方法 当然也可以直接impemnts Iterator 那么Iterable存在的必要? Iterator接口的核心方法是next() hasNext() 是依赖于迭代器当前的迭代位置的 如果Collection直接实现Iterator接口势必导致集合对象包含当前迭代位置的数据 (指针) 党籍和在不同方法之间传递时,由于当前迭代位置不可预知,那么next方法的结果变成不可预知 除非再为Iterator接口添加一个reset方法,用来重置当前迭代位置, 但即使这样COllection也只能同时存在一个当前迭代位置 而Iterable则不然,每次调用都会返回一个从头开始计数的迭代器 多个迭代器是互不干扰的
3. String.contains源码
String.contains(Charsequence) 1. abstract.contains(a) 2. indexof(a.toString) > -1 3. indexof(a, 0) 4. indexof(value, 0, value.length, str.value, 0, str.value.length, fromIndex) 5. value是一个字符数组 是String的内部实现 6. /* * source bstract * sourceOffset 0 * sourceCount 7 * targetac * targetOffset 0 * targetCount 2 * fromIndex 0 * */ public int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex) { if (fromIndex >= sourceCount) { return (targetCount == 0 ? sourceCount : -1); } if (fromIndex < 0) { fromIndex = 0; } if (targetCount == 0) { return fromIndex; } char first = target[targetOffset]; //比方说bstract 找ac 你找到最后一个字母才找到a有什么用呢 所以 最起码你要在倒数第二个找到a 否则 就是找不到 int max = sourceOffset + (sourceCount - targetCount); //第一个if失败或者第二个if失败 都会触发i>max 然后返回-1 还有就是第一个匹配但是其他的不匹配 for (int i = sourceOffset + fromIndex; i <= max; i++) { /* Look for first character. */ if (source[i] != first) { while (++i <= max && source[i] != first); } /* Found first character, now look at the rest of v2 */ if (i <= max) { //判断是否找见第一个字符 int j = i + 1; int end = j + targetCount - 1; for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++); //只有第一个字符 要比较是否找到 其他的通过比较 j 是否进行到了end即可 if (j == end) { /* Found whole string. */ return i - sourceOffset; } } } return -1; }
4. final
final修饰的类有什么特点
Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类、非抽象类成员方法和变量。 •final类不能被继承,没有子类,final类中的方法默认是final的。 •final方法不能被子类的方法覆盖,但可以被继承。 •final成员变量表示常量,只能被赋值一次,赋值后值不再改变。 •final不能用于修饰构造方法。 注意:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。
final类
final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会载被扩展,那么就设计为final类。
final方法
如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final方法。使用final方法的原因有二:
①把方法锁定,防止任何继承类修改它的意义和实现。
②高效,编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。
例如:
public class Test1 { public static void main(String[] args) { // TODO 自动生成方法存根 } public void f1() { System.out.println("f1"); } //无法被子类覆盖的方法 public final void f2() { System.out.println("f2"); } public void f3() { System.out.println("f3"); } private void f4() { System.out.println("f4"); } } public class Test2 extends Test1 { public void f1(){ System.out.println("Test1父类方法f1被覆盖!"); } public static void main(String[] args) { Test2 t=new Test2(); t.f1(); t.f2(); //调用从父类继承过来的final方法 t.f3(); //调用从父类继承过来的方法 //t.f4(); //调用失败,无法从父类继承获得 } }
final变量(常量)
用final修饰的成员变量表示常量,值一旦给定就无法改变;
final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。
从下面的例子中可以看出,一旦给final变量初值后,值就不能再改变了。
package org.leizhimin; public class Test3 { private final String S="final实例变量S"; private final int A=100; public final int B=90; public static final int C=80; private static final int D=70; public final int E; //final空白,必须在初始化对象的时候赋初值 public Test3(int x){ E=x; } /** * @param args */ public static void main(String[] args) { Test3 t=new Test3(2); //t.A=101; //出错,final变量的值一旦给定就无法改变 //t.B=91; //出错,final变量的值一旦给定就无法改变 //t.C=81; //出错,final变量的值一旦给定就无法改变 //t.D=71; //出错,final变量的值一旦给定就无法改变 System.out.println(t.A); System.out.println(t.B); System.out.println(t.C); //不推荐用对象方式访问静态字段 System.out.println(t.D); //不推荐用对象方式访问静态字段 System.out.println(Test3.C); System.out.println(Test3.D); //System.out.println(Test3.E); //出错,因为E为final空白,依据不同对象值有所不同. System.out.println(t.E); Test3 t1=new Test3(3); System.out.println(t1.E); //final空白变量E依据对象的不同而不同 } private void test(){ System.out.println(new Test3(1).A); System.out.println(Test3.C); System.out.println(Test3.D); } public void test2(){ final int a; //final空白,在需要的时候才赋值 final int b=4; //局部常量--final用于局部变量的情形 final int c; //final空白,一直没有给赋值. a=3; //a=4; 出错,已经给赋过值了. //b=2; 出错,已经给赋过值了. } }
另外,final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么
情况,编译器都确保空白final在使用之前必须被初始化。但是,final空白在final关键字final
的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,
却有保持其恒定不变的特征。
final参数
当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。
public class Test4 { public static void main(String[] args) { new Test4().f1(2); } public void f1(final int i){ //i++; //i是final类型的,值不允许改变的. System.out.print(i); }
5. | & ||
|:逻辑或 不管怎样都会执行左右两边 ||: 断言已经能判断真假 就不需要往后继续判断或 strId == null || strId.trim().equals("")
6. java.util.Date & mysql 0000-00-00 0:00:00
java.util.Date d = rs.getTImestamp("");
7. 程序优化
<1>常用逻辑抽象为方法 <2>常用变量声明为final 可以用接口或者枚举实现 <3>内部类 匿名类 外部类 <4>多态 <5>良好的注释 <6>异常的处理 资源的关闭 <7>对象的声明与销毁 <8>问题的解决方案 线程 异步 <9>进阶 设计模式 动态代理 <10>不要再一个类里处理另一个类的逻辑 应该在另一个类封装 <11>几个类有相同的逻辑 定义抽象类 继承 <12> 类内 static 不用每次用的时候都load进内存 <13>根据用户的需求 改变代码 --->配置文件
8. 引用赋值问题
可以让别人传过来 也可以自己过去接受
9.Java中子类与父类的构造方法的调用关系
在 Java 中,无论是 explicit 还是 implicit 方式,都要求在子类的构造方法中调用其父类的构造方法。如果父类无构造方法(其实是一个默认无参的构造方法),那么子类的构造方法中会自动进行调用;如果 父类有自己的构造方法(这时父类不会有默认无参的构造方法),那么在子类的构造方法中,必须要调用父类的某个构造方法,而且必须是在构造方法的第一个语句 中进行调用。 究其原因,想必是 Java 语言设计者,要求子类有责任保证它所继承的父类尽快进入到一个稳定、完整的状态中。试想,如果没有这个约束,那么子类的某个继承自父类的方法可能会使用到父类中的一些变量,而这些变量并没有进行初始化,从而产生一些难以预料的后果,因此构造子类的对象前,必须构造父类的对象,并将之隐含于子类对象之中,使用关键字super引用父类对象。 也因此,当一个类的构造方法是 private 时,它是不可被 extends 的,因为子类构造方法难以调用到这个父类的构造方法。
10 Java堆栈
内存分配的策略
按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的.
静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.
栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。
静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放.
堆和栈的比较
上面的定义从编译原理的教材中总结而来,除静态存储分配之外,都显得很呆板和难以理解,下面撇开静态存储分配,集中比较堆和栈:
从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的,栈主要是用来执行程序的.而这种不同又主要是由于堆和栈的特点决定的:
在编程中,例如C/C++中,所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。实际上也不是什么分配,只是从栈顶向上用就行,就好像工厂中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候,修改栈指针就可以把栈中的内容销毁.这样的模式速度最快,当然要用来运行程序了.需要注意的是,在分配的时候,比如为一个即将要调用的程序模块分配数据区时,应事先知道这个数据区的大小,也就说是虽然分配是在程序运行时进行的,但是分配的大小多少是确定的,不变的,而这个"大小多少"是在编译时确定的,不是在运行时.
堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.在C++中,要求创建一个对象时,只需用new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存.当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!这也正是导致我们刚才所说的效率低的原因,看来列宁同志说的好,人的优点往往也是人的缺点,人的缺点往往也是人的优点(晕~).
- JVM中的堆和栈
JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。
我们知道,某个线程正在执行的方法称为此线程的当前方法.我们可能不知道,当前方法使用的帧称为当前帧。当线程激活一个Java方法,JVM就会在线程的Java堆栈里新压入一个帧。这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据.这个帧在这里和编译原理中的活动纪录的概念是差不多的.
从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。
每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。
11 Java加载顺序
先类的静态初始化,再实例初始化,最后执行构造方法。也就是说,静态初始化是属于类加载的过程,所以它只执行一次,而实例初始化是每个对象创建时都会执行一次,而构造方法跟实例初始化其实也差不多,不过它在实例初始化之后执行,而且构造方法可以重载多个,执行哪个构造方法是根据你的选择来的。
12 StringBuilder的清空
StringBuilder 没有提供clear或empty方法。
清空有3种方法:
1)新生成一个,旧的由系统自动回收
2)使用delete
3)使用setLength
将三种方法循环1000万次,代码:
1.public class sbbm { 2. 3.static String a; 4.static long time ; 5.public static void main( String[] args ) throws Exception { 6. 7. StringBuilder sb = new StringBuilder(); 8. StringBuilder sb3 = new StringBuilder(); 9. 10. time = System.currentTimeMillis(); 11. for( int i = 0; i < 10000000; i++ ) { 12. StringBuilder sb2 = new StringBuilder(); 13. sb2.append( "someStr6ing" ); 14. sb2.append( "someS5tring2" ); 15. sb2.append( "some3Strin4g" ); 16. sb2.append( "so3meStr5ing" ); 17. sb2.append( "so2meSt7ring" ); 18. a = sb2.toString(); 19. } 20. System.out.println( "Way2="+(System.currentTimeMillis()-time) ); 21. 22. 23.time = System.currentTimeMillis(); 24.for( int i = 0; i < 10000000; i++ ) { 25.sb.delete( 0, sb.length() ); 26.sb.append( "someString" ); 27.sb.append( "someString2" ); 28.sb.append( "someStrin4g" ); 29.sb.append( "someStr5ing" ); 30.sb.append( "someSt7ring" ); 31.a = sb.toString(); 32.} 33.System.out.println( "Way1="+(System.currentTimeMillis()-time) ); 34. 35.time = System.currentTimeMillis(); 36.for( int i = 0; i < 10000000; i++ ) { 37. 38.sb3.setLength( 0 ); 39.sb3.append( "someStr55ing" ); 40.sb3.append( "some44String2" ); 41.sb3.append( "som55eStrin4g" ); 42.sb3.append( "some66Str5ing" ); 43.sb3.append( "so33meSt7ring" ); 44.a= sb3.toString() ; 45.} 46.System.out.println( "Way3="+(System.currentTimeMillis()-time) ); 47. 48. 49.} 50.}