java笔记一

用java也几年了,各种书籍电子文档也看了不少,java编程思想的电子书也看过,这次准备系统的在学习学习,巩固一下,顺便做个笔记。如有问题或者理解错误请大家指正,谢谢!
以下概括几个重点:
一.对象
对象,我的理解就是一类事务的具体抽象。例如:猪,房子。学习java的都知道一句名言,“万物皆对象”。就是在java的世界里所有的事务都能抽象成对象,抽象的好与坏决定了代码的质量。对象是一个概念,它的作用是用于我们分析需求,而后设计程序。
它的体现,简单点说,猪是一种动物,狗是一种动物,那在定义实体对象的时候,就会定义一个实体类:Pig和Dog。如果我们在深入的想想,猪和狗都是动物啊,他们都有很多相似的特性,比如:有头、眼睛;都会叫、跑等等。(当然如果有需要,可以将头抽象成一个对象,因为眼睛、耳朵等都是头上的属性)那我们抽象一个动物类:Animal,它里边都会定义很多动物的共同属性和行为。然后Pig和Dog就可以继承他们动物特有的属性和方法。
当然,我们“重写”了eat方法,因为猪和狗,吃东西不一样,猪不吃屎,狗会吃屎。额~~~我乱猜的。
java笔记一_第1张图片
二.“是一个”与“像是一个”
这是设计上的一个争论,例如一个制冷系统。起初这样设计:
java笔记一_第2张图片
空调“是一个”制冷系统,假设这个空调只能制冷,但是热力泵可以替代空调即“像是一个”制冷系统,而且还可以制热。但是对于制冷系统的调用者来说,它只知道有一个cool方法,而压根不知道有个制热功能。
当然,我们可以做一个通用的设计,系统不叫制冷系统,叫“温度控制系统”,这样做是不是更合理呢。但是又有一个问题,对空调来说,他只有一个制冷功能,但是也必须实现heat这个方法,即实现是空的。

java笔记一_第3张图片

这两种设计方式的使用,因情况而定,主要还是看需求。
三.上溯造型(向上转型)
继承和接口实现,有一个特点,父类可以当做子类对象来使用,但是只能调用父类定义的方法或属性。一般来讲,只有用到上溯造型时,才会用继承。
这在我们做程序设计的时候经常用到,尤其各种设计模式。这种面向接口的编程方式的好处就是,调用方面对的总是一类接口,而不关心它的实现,易于代码维护。
例如:上边的温度控制系统,一旦空调换成了热力泵,我只需要给他换一个热力泵。而对于调用温度控制系统的地方,不需要做任何的改动。
例如:策略模式
//温度控制系统
public interface TemperatureControlSystem {
	public void helloWorld();
}
//空调实现
public class AirConditioner implements TemperatureControlSystem {
	@Override
	public void helloWorld() {
		System.out.println("hello~~~~~AirConditioner");
	}
}
//热力泵实现
public class HeatPump implements TemperatureControlSystem {
	@Override
	public void helloWorld() {
		System.out.println("hello~~~~~ HeatPump ");
	}
}
//调用方法
public class Context {
	private TemperatureControlSystem strategy;
	public Context(TemperatureControlSystem strategy) {
		this.strategy = strategy;
	}
	public void doMethod(){
		strategy.helloWorld();
	}
	public static void main(String[] args) {
		Context c ;
		c = new Context(new AirConditioner());
		c.doMethod();
		c = new Context(new HeatPump ());
		c.doMethod();
	}
}

在调用方法中,显而易见,我们只需要将new AirConditioner()换成new HeatPump ()就完成了我们冰箱与热力泵的兑换。
问题:为什么c = new Context(new AirConditioner());中传入的2个对象(虽然他们是实现的同一个接口),而确调用了正确的实现类的方法呢?
原因是,java中有一个机制叫做“绑定”。程序执行前的绑定叫“前期绑定”如:static,final,private(private也就是final的)。运行时绑定叫“动态绑定”。反正想一下就知道,对象转换过程中肯定绑定了真实对象的类型信息。

四.对象的生命周期
说对象的生命周期,先要讲对象创建。
当我们创建一个对象的时候,内存堆中会分配一些动态内存空间给他。对象创建常见方式2种:
int s=”111”;
Person p = new Person();
其中“s”和“p”都是对象的引用,他们是存放在栈内存中的,基本类型对象也都是存放在栈内存中的。“111”也是存放在栈内存中的。只有new出来的对象是存放在堆内存中的。
当一个对象的引用变为0的时候,意味着jvm可以清理它了。
堆栈详解见:http://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html

五.初始化
初始化顺序
用new一个类来说明比较容易,比如:
public class Initialize {

	private static Test t = new Test();
	
	{
		System.out.println("{}");
	}
	
	private int n=2;
	
	static{
		System.out.println("static{}");
	}
	
	public Initialize() {
		System.out.println("initialize:Constructor");
		System.out.println(n);
	}

	static{
		System.out.println("static2{}");
	}
	
	public void f(){
		System.out.println("f():execute");
	}
	
	public static void main(String[] args) {
		new Initialize().f();
		new Test2();
	}

}

class Test{

	public Test() {
		System.out.println("Test:Constructor");
		p();
	}
	
	public void p(){
		System.out.println("Test:m");
	}
}

class Test2 extends Test{
	private int m=1;
	
	public Test2(){
		System.out.println("Test2:Constructor");
	}
	
	public void p(){
		System.out.println("Test2:m:"+m);
	}
}
/*
Test:Constructor
static{}
static2{}
{}
initialize:Constructor
2
f():execute
Test:Constructor
Test2:m:0
Test2:Constructor
*///:~

为什么m的输出会是0呢?因为子类重写了父类的p()方法,而创建子类对象之前,先要创建父类对象,但在父类对象的构造器中又调用了,被子类重写的方法p(),所以调用的是子类的p()方法,而这个时候子类Test2中的m还没有执行到被赋值的地方,所以编译器给了个初始值0。
统一类型的初始化都是按排列顺寻执行。
1.在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。(解释m为什么是0)
2.静态属性、静态块初始化
3.成员变量、基本块初始化
4.构造器初始化
5.局部变量初始化
六.垃圾回收机制
Java中的垃圾回收机制是一个神器,他帮助我们清理不用的对象,而我们不用手动的释放他们。
Jvm的垃圾回收机制只与堆内存打交道,即只管理new出来的对象。当jvm内存不够用时,垃圾回收机制才会运行,清理无用的垃圾对象。
垃圾回收方法原理
对于垃圾回收的方法有很多,常用的是“引用计数”和“标记清零”。这些知识只能帮助我们理解垃圾回收的机制,让我们更好的管理代码。实际的java虚拟机可能用的其他牛逼的方法。
 引用计数法
每个对象都有一个引用计数器,当对象引用增加是加1,引用去除时或被置为null时减1。垃圾回收器在所有对象上遍历,当对象引用变为0时,就释放其占用的空间。这种方法的缺陷是,如果对象循环引用,则会出现“对象该被释放,但是引用不为0的情况”。这种方式用来说明垃圾回收的工作方式。
 标记清理
这种收集器首先遍历对象图并标记可到达的对象,然后扫描堆栈以寻找未标记对象并释放它们的内存。这种收集器一般使用单线程工作并停止其他操作。并且,由于它只是清除了那些未标记的对象,而并没有对标记对象进行压缩,导致会产生大量内存碎片,从而浪费内存
 标记-压缩收集器
有时也叫标记-清除-压缩收集器,与标记-清除收集器有相同的标记阶段。在第二阶段,则把标记对象复制到堆栈的新域中以便压缩堆栈。这种收集器也停止其他操作。
 复制收集器
这种收集器将堆栈分为两个域,常称为半空间。每次仅使用一半的空间,JVM生成的新对象则放在另一半空间中。GC运行时,它把可到达对象复制到另一半空间,从而压缩了堆栈。这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。并且对于指定大小堆来说,需要两倍大小的内存,因为任何时候都只使用其中的一半。
 增量收集器
增量收集器把堆栈分为多个域,每次仅从一个域收集垃圾,也可理解为把堆栈分成一小块一小块,每次仅对某一个块进行垃圾收集。这会造成较小的应用程序中断时间,使得用户一般不能觉察到垃圾收集器正在工作。
 分代收集器
复制收集器的缺点是:每次收集时,所有的标记对象都要被拷贝,从而导致一些生命周期很长的对象被来回拷贝多次,消耗大量的时间。而分代收集器则可解决这个问题,分代收集器把堆栈分为两个或多个域,用以存放不同寿命的对象。JVM生成的新对象一般放在其中的某个域中。过一段时间,继续存在的对象(非短命对象)将获得使用期并转入更长寿命的域中。分代收集器对不同的域使用不同的算法以优化性能。
finalize的用法
finalize方法是Object类的一个方法,多有的类都继承了它可以重写它。
finalize方法只在两种情况下调用,一种是jvm准备垃圾回收,会先调用它,还有就是手动调用System.gc()调用它;但是垃圾回收只和内存有关系,只有内存不够用的时候才会执行回收机制,而finalize或者将对象应用置为null都只是说明此对象可以被回收,但不一定就会被回收。对象引用置为null只是释放了在栈中的内存,并没有释放在堆中new的内存,堆中的内存由jvm垃圾回收控制。
 使用垃圾收集器注意的地方
(1)每个对象只能调用finalize( )方法一次。如果在finalize( )方法执行时产生异常(exception),则该对象仍可以被垃圾收集器收集。
(2)垃圾收集器跟踪每一个对象,收集那些不可触及的对象(即该对象不再被程序引用 了),回收其占有的内存空间。但在进行垃圾收集的时候,垃圾收集器会调用该对象的finalize( )方法(如果有)。如果在finalize()方法中,又使得该对象被程序引用(俗称复活了),则该对象就变成了可触及的对象,暂时不会被垃圾收集了。但是由于每个对象只能调用一次finalize( )方法,所以每个对象也只可能 "复活 "一次。
(3)Java语言允许程序员为任何方法添加finalize( )方法,该方法会在垃圾收集器交换回收对象之前被调用。但不要过分依赖该方法对系统资源进行回收和再利用,因为该方法调用后的执行结果是不可预知的。
(4)垃圾收集器不可以被强制执行,但程序员可以通过调研System.gc方法来建议执行垃圾收集。记住,只是建议。一般不建议自己写System.gc,因为会加大垃圾收集工作量。
public class FinalizeTest {
	@Override
	protected void finalize() throws Throwable {
		System.out.println("finalize called");
	}
	public static void main(String[] args) {
		new FinalizeTest();
		System.gc();
		System.gc();
	}
}
/*output:finalize called*///:~

七.异常
异常是java的又一个牛逼特点。它可以帮助我们在可能发生错误的地方“捕获”异常,防止出现代码卡死的情况;或者处理异常情况,保证代码的“异常流程”正确执行。
异常处理的方式改变了代码的编写方式:
代码一:
/**
	 * 1:正常;0:失败;-1:异常
	 * @return
	 */
	public int excute(){
		try{
			if(true){
				return 1;
			}else{
				return 0;
			}
		}catch(Exception e){
			return -1;
		}
	}代码二:
	public void excute2(){
		try{
			System.out.println("execute");
		}catch(Exception e){
			throw new RuntimeException("出错了"+e);
		}
	}

在程序处理中,我们更愿意采用第二种方式,因为它可以很清楚的把后台错误“抛送”上来,以供前台处理。
注意:
如果在异常上抛过程中,有一层代理,那么就需要再代理中处理异常,然后再向上抛送一次,否则前台获取的错误会是代理异常,而不是底层的异常信息。
例如:
/**
 *通用的限制适配器
 */
	public <T> T getProxy(Class<T> intf, final T obj) {
		return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(),
				new Class[] { intf }, new InvocationHandler() {
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						try{
							Object result =  method.invoke(obj, args);
							return result;
						}catch(Exception e){
							throw e.getCause();
						}
					}
				});
	}

比如,此代理处理的是代码二中的excute2方法,那么在catch中,用throw e.getCause()方法将底层信息在抛送一次。这样在前台的异常信息就会是“"出错了"+e”;而不是java.lang.reflect.xxx的异常信息。
八.shis关键字
this关键字代表了对象本身,在一个类中是隐式存在的。就说说几个常用的形式。
 shis构造器
/*这个类说明了2个问题,一个是this构造器,一个是创建子类对象的时候,必然先创建父类对象,
 * 即在this(1),方法前边默认插入了一个super();
 * 用IDE生成构造器的时候会自动加入,不知道大家注意过没有。*/
public class ExtemdThis extends Father{

	int x;
	
	public ExtemdThis() {
		this(1);//这种调用必须在此方法中的第一样,否则报错。
		super.p();
	}
	
	public ExtemdThis(int x) {
		this.x = x;
	}
	
	public static void main(String[] args) {
		System.out.println(new ExtemdThis().x);
	}
}

class Father{
	protected void p(){
		System.out.println("Father");
	}
}
/*
Father
1
*///:~

 2.传递当前对象
//将当前对象传递给其他方法,为了显示我不是闲的蛋疼,在传递过程中对此对象做了一些处理,给x赋值。
public class PersonEatApple {

	public void eat(Apple apple){
		Apple peeled = apple.getPeeled();
		System.out.println(peeled.x);
	}
	
	public static void main(String[] args) {
		new PersonEatApple().eat(new Apple());
	}
}

class Peeler{
	static Apple peel(Apple apple){
		apple.x=1;
		return apple;
	}
}

class Apple{
	int x;
	
	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	Apple getPeeled(){
		return Peeler.peel(this);
	}
}
/*Output:
 * 1
 * *///:~

 3.返回对象本身引用
//返回对象本身的引用,可以做到非常有趣的事情。
public class ReturnThis {

	int i;
	public ReturnThis increment(){
		i++;
		return this;
	}
	
	public static void main(String[] args) {
		System.out.println(new ReturnThis().increment().increment().increment().i);
	}
}
/*output:3
 *///:~

 4.javabean规范
//javabean中用于区分数据成员和参数名的区别,这算是一种规范。
public class JavaBean {

	int x;

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public JavaBean(int x) {
		super();
		this.x = x;
	}
	
}

你可能感兴趣的:(java笔记一)