很久之前写的一个学习笔记,拿出来看看挺好玩的,不管对错就一顿记,哈哈!
虚拟机查找jar包顺序
虚拟机在运行一个类时,需要将其装入内存,虚拟机搜索类的方式和顺序如下:
Bootstrap classes,Extension classes,User classes。
Bootstrap 中的路径是虚拟机自带的 jar 或 zip 文件,虚拟机首先搜索这些包文件,用
System.getProperty("sun.boot.class.path")可得到虚拟机搜索的包名。
Extension 是位于 jre\lib\ext 目录下的 jar 文件,
虚拟机在搜索完 Bootstrap 后就搜索该
目 录 下 的 jar 文 件 。用 System. getProperty("java.ext.dirs ") 可得 到 虚 拟 机 使 用
Extension 搜索路径。
User classes 搜索顺序为当前目录、环境变量 CLASSPATH、-classpath。
内存处理
- java内存一些内存处理
- 主要分为stack和heap,其中stack的速度比较快,不过由于存储结构的问题要求存入其中的内容占用的内存大小必须是确定的,heap中的内存是动态扩展的。所以对于程序执行过程中的类来说,没有办法保存在stack中(同时也有占用空间比较多的问题),所以采取的方式是在stack中保存类在heap中的地址,在用的时候去heap里面取值。通常来说stack中保存的是有明确声明周期(如局部变量等),所以不需要执行垃圾回收。在堆中需要执行垃圾回收。
- int a = 3;int b=3;int a=4;处理方式是首先在stack中寻找是否有3这个值,没有,然后在常量区存入3的值,然后让a等于它,在定义b的时候发现已经存在3了,就不去在创建常量3,而是直接让它等于,然后给a复制为4的时候,发现常量区没有4,在加入4,让a等于这个值。
java泛型
首先来说和c++中类似
import Java.util.Hashtable; class TestGen0<K,V>{ public Hashtable<K,V> h=new Hashtable<K,V>(); public void put(K k, V v) { h.put(k,v); } public V get(K k) { return h.get(k); } public static void main(String args[]){ TestGen0<String,String> t=new TestGen0<String,String>(); t.put("key", "value"); String s=t.get("key"); System.out.println(s); } }
注意:List<String>并不是List<Object>的子类,在传参数的时候不能认为Lst<Object>的参数对于所有List<>都是可以的。
class TestGen2<K extents String,V extends Number>
上面那个的代码可以对传入的类型做一些限制,限制K必须<=String,V的类型必须<=Number(<=的意思就是是当前类或者是其子类)
class C<T extends Comparable<? super T> & Serializable>
上面的代码中T extends Comparable这个是对上限的限制,Comparable<? super T>这个是下限的限制,Serializable是第2个上限。一个指定的类型参数可以具有一个或多个上限。具有多重限制的类型参数可以用于访问它的每个限制的方法和域。
需要注意的一些问题:
public void upperBound(List<? extends Date> list, Date date) { Date now = list.get(0); System.out.println("now==>" + now); //list.add(date); //这句话无法编译 list.add(null);//这句可以编译,因为null没有类型信息 }
上述代码不能编译的原因是list中可以是Date类型也可以是Date的子类,如果是Date的子类把Date的值放过去当然是不可以的。解决方法可以是:
public <T extends Date> void upperBound2(List<T> list, T date) { list.add(date); }
这样就能保证传进来的date的类型一定和list的类型是兼容的。
类似的问题在限制下限的时候也会存在
public void lowerBound(List<? super Timestamp> list) { Timestamp now = new Timestamp(System.currentTimeMillis()); list.add(now); //Timestamp time = list.get(0); //不能编译 }
原因是list中的值可以是Timestamp的基类,而由基类转化为子类当然是不可以的
java枚举
定义一个枚举类
public enum Color{ RED,BLUE,BLACK,YELLOW,GREEN }
可以看出enum和class的用法相似,实际上enum声明定义的类型就是一个类。而这些类都是类库中Enum类的子类(java.lang.Enum<E>)
Color枚举类是特殊的class,其枚举值(RED,BLUE...)是Color的类对象(类实例):Color c=Color.RED;而且这些枚举值都是public static final的,也就是我们经常所定义的常量方式,因此枚举类中的枚举值最好全部大写。
枚举类是class,枚举类型中可以有构造器,方法和数据域。但是,枚举类的构造器只有在构造枚举值的时候被调用,必须是private的构造方法,保证不会被外部调用。
enum Color{ RED(255,0,0),BLUE(0,0,255),BLACK(0,0,0),YELLOW(255,255,0),GREEN(0,255,0); //构造枚举值 private Color(int rv,int gv,int bv){ this.redValue=rv; this.greenValue=gv; this.blueValue=bv; } public String toString(){ //自定义的public方法 return super.toString()+"("+redValue+","+greenValue+","+blueValue+")"; } private int redValue; //自定义数据域,private为了封装。 private int greenValue; private int blueValue; }
可以在实例中对abstract方法进行不同的实现
public enum TestEnumMathod { //为每个enum实例添加不同的实现方法 SAMPLE1 { String getInfo() { return "SAMPLE1"; } }, SAMPLE2{ String getInfo() { return "SAMPLE2"; } }; abstract String getInfo(); }
enum类中的方法:
- Color.RED.ordinal():返回RED在枚举类中的定义序号,返回值0
- compareTo:返回两个实例之间的序号差,要求必须是同一枚举类型,否则抛出异常
- Color[] colors=Color.values():返回枚举类中的所有枚举实例
注意:枚举类可以被switch
java自动装拆包
存在的问题:
public static void main(String[] args) { Integer i1=100; Integer i2=100; if(i1==i2) System.out.println("i1==i2"); else System.out.println("i1!=i2"); /*输出为 i1=i2*/ // Integer i1=200; // Integer i2=200; // if(i1==i2) // System.out.println("i1==i2"); // else // System.out.println("i1!=i2"); /*输出为 i1!=i2*/ }
原因:
/* * 返回一个表示指定的 int 值的 Integer 实例。如果不需要新的 Integer 实例,则 * 通常应优先使用该方法,而不是构造方法 Integer(int),因为该方法有可能通过 * 缓存经常请求的值而显著提高空间和时间性能。 * @param i an <code>int</code> value. * @return a <tt>Integer</tt> instance representing <tt>i</tt>. * @since 1.5 */ public static Integer valueOf(int i) { final int offset = 128; if (i >= -128 && i <= 127) { // must cache return IntegerCache.cache[i + offset]; } return new Integer(i); } /* * IntegerCache内部类 * 其中cache[]数组用于存放从-128到127一共256个整数 */ 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(i - 128); } }
在不是现实的去new一个integer的时候,自动选择valueOf这个方法。
java可变参数
通过对可变参数的应用,可以对传入的参数进行一些动态的控制。
public void write(Object... objs) { for (Object obj: objs) System.out.println(obj); } util.write(obj1); util.write(obj1,obj2); util.write(obj1,obj2,obj3);
重载时注意的问题:
1.public void hello(String ...params); 2.public void hello(String[] params); 3.public void hello(String param1, String ...params) 4.public void hello()
上面的函数中,1和2是不能同时定义在一起的,系统认为这两个是同一个函数。
1和3同时存在的时候,如调用hello("1"),hello("1","2")之类的都会编译不通过,因为系统不知道应该去
调用哪一个函数。
1和4同时的时候会去调用4,原则是有明确与之对应的参数列表的重载函数优先,然后再去调用可变参数的。
注意:可变参数只能是参数列表的最后一个。
java静态导入
通过java静态导入可以很方便的使用导入的static变量或方法,不过这样会有很多困扰,所以这里尽量仅导入需要的。
import static tips.Constants.PORT_NUMBER; import static tips.gen.App.DEFAULT_URL;
java关键字
- native
- native是方法修饰符。Native方法是由另外一种语言(如 c/c++,FORTRAN,汇编)实现的本地方法。因为在外部实现了方法,所以在java代码中,就不需要声明了,有点类似于借口方法。Native可以和其他一些修饰符连用,但是abstract方法和Interface方法不能用native来修饰,因为native默认是方法有实现体的。当一个类第一次被使用到时,这个类的字节码会被加载到内存,并且只会回载一次。在这个被加载的字节码的入口维持着一个该类所有方法描述符的list,这些方法描述符包含这样一些信息:方法代码存于何处,它有哪些参数,方法的描述符(public之类)等等。如果一个方法描述符内有native,这个描述符块将有一个指向该方法的实现的指针。这些实现在一些DLL文件内,但是它们会被操作系统加载到java程序的地址空间。当一个带有本地方法的类被加载时,其相关的DLL并未被加载,因此指向方法实现的指针并不会被设置。当本地方法被调用之前,这些DLL才会被加载,这是通过调用java.system.loadLibrary()实现的。
native public void Native1( int x ) ;
- native是方法修饰符。Native方法是由另外一种语言(如 c/c++,FORTRAN,汇编)实现的本地方法。因为在外部实现了方法,所以在java代码中,就不需要声明了,有点类似于借口方法。Native可以和其他一些修饰符连用,但是abstract方法和Interface方法不能用native来修饰,因为native默认是方法有实现体的。当一个类第一次被使用到时,这个类的字节码会被加载到内存,并且只会回载一次。在这个被加载的字节码的入口维持着一个该类所有方法描述符的list,这些方法描述符包含这样一些信息:方法代码存于何处,它有哪些参数,方法的描述符(public之类)等等。如果一个方法描述符内有native,这个描述符块将有一个指向该方法的实现的指针。这些实现在一些DLL文件内,但是它们会被操作系统加载到java程序的地址空间。当一个带有本地方法的类被加载时,其相关的DLL并未被加载,因此指向方法实现的指针并不会被设置。当本地方法被调用之前,这些DLL才会被加载,这是通过调用java.system.loadLibrary()实现的。
- strictfp
- 修饰类和方法,意思是FP-strict,精确浮点。当JAVA虚拟机进行浮点运算时,如果没有指定strictfp关键字时,JAVA的编译器以及运行环境在对浮点运算的表达式是采取一种莫名其妙的方式运行。而一旦使用了strictfp来声明一个类、接口或者方法时,那么所声明的范围内JAVA的编译器以及运行环境会完全依照浮点规范IEEE-754来执行。因此如果你想让你的浮点运算更加精确,而且不会因为不同的硬件平台所执行的结果不一致的话,那就请用关键字strictfp。。当一个class或interface用strictfp声明,内部所有的float和double表达式都会成为strictfp的。
strictfp interface FPTest
- 修饰类和方法,意思是FP-strict,精确浮点。当JAVA虚拟机进行浮点运算时,如果没有指定strictfp关键字时,JAVA的编译器以及运行环境在对浮点运算的表达式是采取一种莫名其妙的方式运行。而一旦使用了strictfp来声明一个类、接口或者方法时,那么所声明的范围内JAVA的编译器以及运行环境会完全依照浮点规范IEEE-754来执行。因此如果你想让你的浮点运算更加精确,而且不会因为不同的硬件平台所执行的结果不一致的话,那就请用关键字strictfp。。当一个class或interface用strictfp声明,内部所有的float和double表达式都会成为strictfp的。
- transient
- 变量修饰符(只能修饰字段)。标记为transient的变量。当对象序列化的保存在存储器上时,不希望有些字段数据被保存,为了保证安全性,可以把这些字段声明为transient。
- volatile
- volatile修饰变量。在每次被线程访问时,都强迫从内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。