Initialization & Cleanup

方法重载(overload)参数为基本类型的状况 java

方法重载时若是一系列名字相同的方法接受的参数的类型是不同的基本类型,那么重载方法的选择规则是,将接收的参数一级一级向上提高,直到找到合适的方法为止,不然编译出错。 c++

public static void main(String args[]){
        char c ='a';
        new Test2().f1(c);//int
   } 
        public void f1(byte c){
		System.out.println("byte");
	}
	public void f1(int c){
		System.out.println("int");
	}
	public void f1(float c){
		System.out.println("float");
	}
	public void f1(double c){
		System.out.println("char");
	}
	public void f1(long c){
		System.out.println("long");
	}
上面这个例子,char向上提高最近的一个是int,因此选择f1(int)方法。
public static void main(String args[]){
		double d = 10.34;
		new Test2().f1((float)d);
	}

	public void f1(byte c){
		System.out.println("byte");
	}
	public void f1(int c){
		System.out.println("int");
	}
	public void f1(float c){
		System.out.println("float");
	}
上面这个例子,必须将double强制转换成下面的类型,不然编译出错,由于找不到合适的方法。


java的构造函数 程序员

java中若是咱们写一个类时没有写构造函数,则java编译器会提供一个默认的无参数的构造函数。 数组

若是咱们提供了构造函数,则不会有默认的,这时若是咱们还想要默认的就必须显示的写进去。  app

java中的this jvm

java中的this指向当前的对象,在对象内部的方法中调用其它内部方法时不须要使用this,这个时候尽可能不要加上this,有个原则:为了程序简洁易读能不写this就不要写。 函数

this还能够用于在一个构造函数中调用同个类的另一个构造函数。 this

public class Test3 {
	private int i;
	private String s;
	public Test3(int iv){
		i = iv;
	}
	public Test3(String sv){
		s =sv;
	}
	public Test3(String sv,int iv){
		this(sv);
	//	this(iv);//不能调用两次
	}
	
	public Test3(){
		int j=10;
	//	this(j);//必须是第一句
	}
	
	public void go(){
	//	this(4);//不在构造函数内
	}
}
从上面这个例子能够看出:

1. 使用this方式调用构造函数只能出如今构造函数中 lua

2. 使用this方式调用构造函数必须出如今构造函数的第一句,而不能出如今后面。 spa

3. 在一个构造函数中经过this调用另外的构造函数只能调用一次,若是调用屡次会编译出错。

java中的static

java中的static方法中不能调用非static的方法和变量,可是若是传个引用的参数过来或者在方法中new一个对象获得引用,就能够。

public class Test4 {
	public static void testStatic(SubTest st){//传个引用
		st.nomal();
	}
	public static void testStatic(){
		SubTest st = new SubTest();//new个对象
		st.nomal();
	}
	
}
class SubTest{
	public void nomal(){
			
	}
}
java中的finalize()和垃圾回收

当java的垃圾回收器决定回收堆内存的时候,首先会调用要回收的对象的finalize方法,接着才开始回收堆内存。

java中全部的对象自己都在堆上,垃圾回收器能够回收它们的内存,为何还要有finalize方法呢,在回收方面finalize惟一可能的用途就是当java使用native方法调用C或C++代码的时候,C或C++代码中的malloc会在堆中分配内存,这个时候必须在finalize中调用一个native方法,这个native方法中会经过free去释放。

finalize和c++中的析构函数是不同的。

C++中对象内存分配会有两种状况,一种是经过相似于声明的方式定义的对象,这种对象分配在栈上,会在对象的做用域结束的时候调用对象的析构函数,还有一种是使用new定义的对象,这种对象分配在堆上,必须经过程序员写出delete代码来销毁。总之能保证对象空间都被实时回收。

而java的finalize和垃圾回收并不必定会执行,垃圾回收只有在垃圾回收器以为内存快不够的时候才会回收,若是程序结束时系统内存还不少,那么垃圾回收器不会进行回收工做,程序结束后对象的内存虽然获得释放,可是finalize就没有机会执行了。

咱们可使用System.gc()来建议垃圾回收器进行回收,但仅仅是建议,垃圾回收器并不必定会回收。

使用finalize除了用于调用native方法回收C和C++的对象内存还能够进行终止状态验证,有的时候咱们要求对象在结束时某个状态必定得是某个值,为了检验防止出错,能够写在finalize中,在finalize中进行验证,虽然执行一次finalize不必定会执行,可是程序运行的次数增长后总会执行finalize,这个时候若是状态有错就能检测到。

java的垃圾回收器是如何工做的

java在堆上分配对象速度很快,比C++在堆上分配对象速度快得多,接近于C++在栈上分配对象的速度。

java之因此在堆上分配内存速度快,是由于垃圾回收器的存在。垃圾回收器一边回收一边紧缩堆中的对象。

垃圾回收器可能的工做方式有:

1.reference counting

缺点是速度慢,而且若是有环状的引用链,虽然引用数目不是0,可是仍是应该回收,这个时候会出问题。这个方法用的很少,几乎没有回收器会使用。

2.任何存活的堆对象均可以追溯到栈中或静态存储区域中的某个引用,这个链可能通过几层对象,但最终必定能找到。所以顺着栈中或静态存储区中的引用纵向寻找,就会找到全部存活的对象,而剩下的就能够当垃圾处理了。对于环状引用的状况,这种机制找不到,能够正确的看成垃圾来处理。

对于第二种方案,jvm使用自适应性的垃圾回收方案,使用stop-and-copy和mark-and-sweep交叉的方式来回收。

stop-and-copy指的是当回收时,首先中止程序的运行,而后将存活的堆对象复制到新的堆中,留在原来的堆中的对象就都是垃圾对象,能够被回收,而新堆中的对象是紧密排列的,至关于作了压缩。这种方法有两点缺点,一是要维护两个堆,浪费一倍空间,为了解决这一问题好多jvm以恰好知足须要的大小的块为单位来分配堆。第二就是当堆内存逐渐趋于稳定,没有什么大的垃圾产生时,复制进程仍然会将一个堆中的对象拷贝到另外一个堆中,这样浪费了空间和时间。

这个时候就须要mark-and-sweep方法了,早期的jvm版本一直使用mark-and-sweep方法,这种方法对广泛状况来讲要慢一些,可是当堆趋于稳定的时候要快一些。这种方法也是从堆中或静态存储区中的引用开始寻找,每当找到一个存活对象就作一个标记,当整个遍历过程结束以后便开始清理。

stop-and-copy和mark-and-sweep都须要先中止程序再开始清理,而不是在后台执行清理工做。

如前文所述,在这里所讨论的Java虚拟机中,内存分配以较大的“块”为单位。若是对象较大,它会占用单独的块。严格来讲,“中止-复制”要求在释放旧有对象以前,必须先把全部存活对象从旧堆复制到新堆,这将致使大量内存复制行为。有了块以后,垃圾回收器在回收的时候就能够往未被使用的块里拷贝对象了。每一个块都用相应的代数(generation count)来记录它是否还存活。一般,若是块在某处被引用,其代数会增长。垃圾回收器将对上次回收动做以后新分配的块进行整理,这对处理大量短命的临时对象颇有帮助。垃圾回收器会按期进行完整的清理动做——大型对象仍然不会被复制(只是其代数会增长),内含小型对象的那些块则被复制并整理。

jvm会监视垃圾回收的效率,若是当前堆趋于稳定不怎么产生垃圾,则转向mark-and-sweep策略,若是堆变得琐碎,有许多垃圾产生,则转向stop-and-copy策略,所以说是自适应的。

JIT编译器

JIT编译器部分或所有将程序转化成本地机器代码,这样就不须要被JVM解释执行,而是直接被机器执行,从而加快了运行速度。

使用JIT编译器一种方案就是使用JIT编译器编译全部的代码。这样有两个缺点:一是增长了编译时间,二是增长了可执行代码的空间。

另外一种方案是lazy evaluation,当运行时第一次建立某个类的实例的时候,那个类会被加载,class文件被读取,字节码被加载到内存中,只有在代码运行的时候才会JIT编译。这样不会被执行的代码永远不会JIT编译。

java成员初始化

Java能够保证任何变量在使用前必定会被初始化,若是有方法中的局部变量没有初始化就使用则编译器会报错。

对于类的成员变量,若是是基础类型,则初始化为0及0对应的值,好比boolean为false,char为空格,以此类推;若是是引用,则初始化为null。

这和C++是不一样的,C++对于基础类型,若是局部变量没有初始化则提供一个随机值,若是全局变量没初始化则提供默认值0,若是成员变量没初始化则要看类的位置,若是是全局变量的类则赋默认值0,若是是局部或类的成员则赋随机值。对于类,不论在局部变量仍是全局变量仍是成员变量都会调用无参数的构造函数初始化,若是没有无参数的构造函数则会编译错误,若是无参数的构造函数没有对类中某个成员变量初始化,若是这个类是全局变量则那个变量会被赋默认的0,若是那个类是局部的或另外一个类的成员,则那个变量会赋随机值。

java中类的成员变量能够在定义给出初始化的值,这在C++中会编译出错:

public class InitialValues2{
  boolean bool = true;
  char ch = 'x';
  byte b = 47;
  Student stu = new Student();
  ...
}
也能够用方法来初始化:
public class InitialValues3{
   public int i = f();
   public int f(){
      return 11;
   }
}
用于初始化的方法能够有参数,可是不能传递给这些方法还没有定义的类的成员变量做为参数:
public class InitialValues4{
   public int i = f(j);//错误
   int j = 10;
   public int m = f(j);//正确
   public int f(int j){
     return j*10;
   }
}
在构造函数中初始化时,类的成员变量首先会默认初始化为0及0对应的值,接着再被初始化为构造函数中给的值。
public class Counter{
   int i;
   public Counter(){
      i=7;
   }
}
对于上面这个例子,i首先初始化为0,接着初始化为7。


初始化的顺序

在一个类中,变量初始化的顺序取决于它们在类中定义的前后顺序,变量的定义能够分布在类的各个地方,包括在两个方法定义之间,成员变量老是会在全部方法被调用以前初始化,包括构造方法。

public class Test7 {
	public static void main(String[] args) {
		B b = new B();
	}
}
class A{
	A(){
		System.out.println("A");
	}
}
class B{
	A a1 = new A();
	B(){
		System.out.println("B");
	}
	A a2 = new A();
}
上面的例子输出A,A,B,说明a1,a2在B的构造方法被调用以前初始化。

static数据的初始化

static关键字不能用于局部变量,所以只能用于成员变量。若是你不初始化一个static的成员变量,若是它是基础类型则获得标准初始值,若是是引用则初始化为null。

初始化的顺序:static的先初始化,若是它们没有由于以前的对象建立而初始化,而后是非静态的对象初始化。

public class Test8 {
	public static void main(String[] args) {
		new C();
		new C();
	}
}
class A1 {
	public A1(String s){
		System.out.println("A1:"+s);
	}
}
class A2 {
	public A2(String s){
		System.out.println("A2:"+s);
	}
}
class C{
	public static A1 a1 = new A1("a1");
	public A1 a2 = new A1("a2");
	public static A2 a3 = new A2("a3"); 
}
上面的例子输出:A1:a1,A2:a3,A1:a2,A1:a2。可见static的a1和a3先被初始化,而后是a2,在第二个C建立的时候,static的a1和a3已经被初始化过了,所以不用再初始化,只有a2再被初始化。

建立对象的过程

1.虽然构造方法没有标识为static的,但其实是static的。以Dog对象为例,当一个对象被建立时或者一个Dog类的静态的属性或方法被访问时,java解释器必须定位Dog.class。

2.Dog.class被加载,它全部的静态部分进行初始化。所以static的属性在类第一次被加载时初始化。

3.当new一个Dog时,Dog的构造进程首先在堆上为Dog分配足够的空间。

4.这个空间初始化为0,至关于对象的变量默认初始化为0及对应值。

5.在字段定义时显示的初始化被执行。

6.构造器链被执行。

显示的static初始化代码段

public class Spoon{ static int i; static{ i = 47; } }

和静态变量初始化同样,static初始化块中的语句只有在所在类被建立或所在类中静态属性或静态方法被访问的时候才会执行,只在最初访问时执行一次。

非静态的实例初始化代码段

java提供了和static初始化代码段相似的非static初始化代码段,用来初始化非静态变量。

public class Test16 {
	{
		i = 10;
		j = 20;
	}
	int i,j;
}
这种代码使得不管哪一个构造方法被调用,实例的变量都能在构造方法执行以前初始化。

static和非static的代码初始化段均可以写在变量声明的前面,就像上面Test16那样。

java的Array

java的Array定义的方式:int[] a1;或int a1[]。

这两种定义方式都只是为一个指向数组的引用分配了空间,而且肯定这个数组只能存放int,没有真正的初始化数组。

能够经过以下方式初始化数组:

int[] a1 = {1,2,3,4,5};

int a2 = a1;//a2与a1指向同一个数组

若是能够肯定长度,则使用int[] a = new int[20];来初始化,这样的初始化若是是基础类型则默认初始化为0及对应值,若是是引用则初始化为null。


Integer[] ia = new Integer[10];
		System.out.println(ia[0]);//null
对于上面的例子,当初始化非基础类型的数组时,只是初始化了一个引用的数组,而且引用的值都是null。


经过autobox机制,能够给ia放入int,例如:


Integer[] ia = new Integer[10];
		ia[0] = new Integer(0);
		ia[1] = 1;//autoboxing
		System.out.println(ia[0]);
		System.out.println(ia[1]);


对于非基础类型的对象的数组,也能够用大括号的方式初始化,以下:

 Integer[] a = { new Integer(1), new Integer(2), 3//autoboxing }; System.out.println(Arrays.toString(a));//[1,2,3] Integer[] b = new Integer[]{ new Integer(1), new Integer(2), 3//autoboxing }; System.out.println(Arrays.toString(b));//[1,2,3]

 大括号初始化方式的例子:

public class Test20 {
	public static void main(String args[]){
		Test21.main(new String[]{"abc","123","abc123"});
	}
}
class Test21{
	public static void main(String args[]){
		System.out.println(Arrays.toString(args));//[abc, 123, abc123]
	}

Arrays.toString格式化数组

java.util包中的Arrays的toString方法能够将一维数组格式化为先后中括号,中间逗号分隔的形式。

int[] a = new int[20];
for(int i=0;i<a.length;i++){
	a[i] = i;
}
System.out.println(Arrays.toString(a));//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
java的可变参数列表
public static void main(String[] args) {
		// TODO Auto-generated method stub
		testVarargs(1,2,3);//能够传逗号分隔的
		ArrayList<Integer> list = new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		list.add(3);
		//testVarargs(list);//不能传list
		testVarargs(new Integer[]{1,2,3});//能够传数组
	}
	
	public static void testVarargs(Integer ... iv){
		for(int i:iv){
			System.out.print(i);
		}
		System.out.println();
	}

注意:若是方法参数要求可变参数列表,则能够传逗号分隔的,也能够传数组;但若是方法参数要求数组,则只能传数组,不能穿逗号分隔的。

可变参数列表方法的重载

public static void main(String[] args) {
		// TODO Auto-generated method stub
		f('a','b');//first
		//f();wrong
		f(1,2);//second
		//f('a',1);The method f(Character...) in the type Temp11 is not applicable for the arguments (char, int)
		//f(1,'a');同上
		//f('a',new Integer(1));基本同上
		//f(new Integer(1),'a');基本同上
	}
	public static void f(Character... chs){
		System.out.println("first");
	}
	public static void f(Integer ... is){
		System.out.println("second");
	}
从上面代码看出,重载仍是找类型最接近的,f()有歧义,所以编译不经过。至于最后几个不经过,好比f('a',1),是由于虽然1能够autobox成Integer,可是'a'不能转成Integer,因此不能按照second f,1不能自动转Character,因此不能按照first f。若是把second f的参数改为int,则只有f('a','b')和f()编译不经过,f('a','b')不经过是由于'a'既能autobox成Character又能自动转成int,于是有歧义。若是把first f改为char,则 f('a','b')不会编译出错,由于找到了最优匹配。

public static void main(String[] args) {
		// TODO Auto-generated method stub
		f(1,'a');//first
		//f('a','b');The method f(float, Character[]) is ambiguous for the type Test12
	}
	
	public static void f(float  i,Character ... args){
		System.out.println("first");
	}
	
	public static void f(Character ... args){
		System.out.println("second");
	}
上面这个例子,f(1,'a')之因此选择first是由于1能够自动转成float而不能自动转成Character,若是float改为Float则编译出错,由于1不能自动转Float和Character。f('a','b')出错的缘由是既能够autobox成Character又能够自动转float,出现歧义。

总之要注意:

1.函数重载老是找最匹配的。

2.基础类型只能包装成对应的那个类,不能转成别的包装类型,当autobox与自动向上转换共存时,就会出现歧义。

java数组类型的强制转换

java数组类型的强制转换方法以下:

Integer[] ia = new Integer[10];
 Object[] da = (Object[])ia;
只有有继承关系的数组才能够强制转换,Integer数组不能转成Double数组。

System.out.println打印某个类的实例

默认状况下,使用System.out.println打印某个类的实例时,会输出实例的类名+@+十六进制表示的实例的地址。

若是本身实现覆盖了Object的toString()方法,则会打印toString()放回的字符串。

Object的getClass方法

Object的getClass方法返回实例对应的类,打印那个类将返回对应的类的名字及相关信息。

System.out.println(t14.getClass());//class test.Test14
int[] a = {1,2,3};
System.out.println(a.getClass());//class [I

对于最后一个,[表示是后面类型的数组,I表示基础类型int。

main函数的两种写法

1.public static void main(String[] args)

2.public static void main(String ... args)

枚举类型

当咱们建立一个枚举类型时,编译器会自动帮咱们产生三个方法:toString()返回枚举类型的实例的名字,ordinal()放回枚举类型某个实例值的顺序,static values()按顺序返回枚举类型全部的值的数组。

enum常常和switch一块儿用,更有意义。

例子:

public class Test16 {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		State active = State.ACTIVE;
		System.out.println(active);//调用toString()输出ACTIVE
		for(State state:State.values()){
			System.out.println(state+" order is "+state.ordinal());
			/*
			 *  REGISTERED order is 0
				ACTIVE order is 1
				BINDED order is 2
				LOCKED order is 3
			 */
		}
		switch(active){
		case REGISTERED:
			//...
			break;
		case ACTIVE:
			//...
			break;
		case BINDED:
			//...
			break;
		case LOCKED:
			//...
			break;
		default:
			//..
			break;
		}
	}

}

enum State{
	REGISTERED,ACTIVE,BINDED,LOCKED
}
相关文章
相关标签/搜索