标签:
抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。
继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。
封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。
多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。
修饰符 | 同一个类中 | 同一个包中 | 子类中 | 全局 |
Private | Y | |||
Default | Y | Y | ||
Protected | Y | Y | Y | |
Public | Y | Y | Y | Y |
1. 一个永不改变的编译时常量
2. 一个在运行时被初始化的值,而你不希望它改变
被final关键字修饰的基本类型数据不能被改变
被final关键字修饰的对象的引用不能被改变,但对象其自身却是可以被修改的。
意味着你无法在方法中更改参数引用所指向的对象。
使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义。这是出于设计的考虑:想要确保在继承中使方法行为保持不变,并且不会被覆盖。第二个原因是效率问题,在Java的早期版本建议使用,现不推荐。
被定义成final的类,不能被继承。换句话说,出于某种考虑,你对该类的设计永不需要做任何变动,或者出于安全的考虑,你不希望它有子类。Final类中所有的方法都隐式指定为final的,因为无法覆盖它们。
i) 创建内部类的方式——把类的定义置于外围类的里面:
public class Parcel1 { //定义内部类 class Destination { private String label; Destination(String whereTo) { label = whereTo; } String readLabel() { return label; } } public void ship(String dest) { //在外围类的非静态方法中创建内部类对象 Destination d = new Destination(dest); System.out.println(d.readLabel()); } public static void main(String[] args) { Parcel1 p = new Parcel1(); p.ship("Tasmania"); } } |
当我们在ship()方法里面使用内部类的时候,与使用普通类没什么不同。实际的区别只是内部类的名字是嵌套在Parcel1里面的。
ii) 使用.this与.new
如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟.this。这样产生的引用自动地具有正确的类型,这一点在编译期就被知晓并受到检查,因此没有任何运行时开销。示例代码如下:
public class DotThis { void f() { System.out.println("DotThis.f()"); } public class Inner { public DotThis outer() { return DotThis.this; } } public Inner inner() { return new Inner(); } public static void main(String[] args) { DotThis dt = new DotThis(); DotThis.Inner dti = dt.inner(); } } |
有时你可能想要告知某些其他对象,去创建其某个内部的对象。要实现此目的,你必须在new表达式中提供对其他外部类对象的引用,这时需要使用.new语法,如下:
public class DotNew { public class Inner {} public static void main(String[] args) { DotNew dn = new DotNew(); DotNew.Inner dni = dn.new Inner(); } } |
定义:在一个方法里面或者任意的作用域内定义的内部类。
i) 在方法的作用域内定义内部类。
public class Parcel5 { public Destination destination(String s) { class PDestination implements Destination { private String label; private PDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } return new PDestination(s); } public static void main(String[] args) { Parcel5 p = new Parcel5(); Destination d = p.destination("Tasmania"); } } |
ii) 在任意作用域内定义内部类。
public class Parcel6 { private void internalTracking(boolean b) { if(b) { class TrackingSlip { private String id; TrackingSlip(String s) { id = s; } String getSlip() { return id; } } TrackingSlip ts = new TrackingSlip("slip"); String s = ts.getSlip(); } } public void track() { internalTracking(true); } public static void main(String[] args) { Parcel6 p = new Parcel6(); p.track(); } } |
i) 通过实现一个接口来定义匿名内部类。
public interface Contents { int value(); } public class Parcel7 { public Contents contents() { //定义匿名内部类 return new Contents() { private int i = 11; public int value() { return i; } }; } } |
该内部类实现了接口Contents,但是没有名字,它是匿名的。
ii) 通过继承一个类来定义匿名内部类。
public class Wrapping { private int i; public Wrapping(int x) { i = x; } public int value() { return i; } } public class Parcel8 { public Wrapping wrapping(int x) { return new Wrapping(x) { public int value() { return super.value(); } }; } } |
该内部类继承自父类Wrapping并覆盖了其方法wrapping(),同样该内部类也是匿名的。
iii) 引用外部类变量
如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的,否则会出现编译错误。请看示例代码:
public class Parcel9 { public Destination destination(final String dest) { return new Destination() { private String label = dest; public String readLabel() { return label; } }; } } |
iv) 匿名内部类的实例初始化
在匿名内部类中不可能有命名构造器,但通过实例初始化,就能够达到为匿名内部类创建一个构造器的效果,如下:
public class Parcel9 { public Destination destination(final String dest) { return new Destination() { {System.out.println("初始化内部类");} @Override public String readLabel() { return null; } }; } } |
但是这样的构造器还是有限制的,你不能重载实例初始化方法,所以你仅有一个这样的构造器。
总结:匿名内部类与正规的继承相比有些受限,因为匿名内部类既可以扩展类,也可以实现接口,但是不能两者兼备。而且如果是实现接口,也只能实现一个接口。
i) 定义
如果不需要内部类对象与外围类对象之间有联系,那么可以将内部类声明为static。这通常称为嵌套类。
嵌套类意味着:
a) 要创建嵌套类的对象,并不需要其外围类的对象。
b) 不能从嵌套类的对象中访问非静态的外围类对象。
c) 普通类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类。但是嵌套类可以包含所有这些东西。
ii) 接口内部的类
正常情况下,不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分。你放到接口中的任何类都自动地是public和static的。因为类是static的,只是将嵌套类置于接口的命名空间内,这并不违反接口的规则。你甚至可以在内部类中实现其外围类接口,就像下面这样:
public interface ClassInInterface { void howdy(); class Test implements ClassInInterface { public void howdy() { System.out.println("Howdy!"); } public static void main(String[] args) { new Test().howdy(); } } } |
如果你想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所共用,那么使用接口内部的嵌套类会显得很方便。
iii) 从多层嵌套类中访问外部类的成员
内部实现,红黑树数据结构原理
内部实现,散列表数据结构
散列冲突的问题
内部实现和用处
数据结构及内部实现
i) Hash table是基于线程安全实现的,Hash map不是。
ii) Hash table key和value不允许null值,Hash map key和value都允许。
iii) Hash table使用Enumeration,Hash map使用Iterator。
iv) 其它的不同需要查看API文档和源代码
i) New (新建状态)
新建一个线程往往是通过 new Thread(r) 这种方法。新建一个线程并不意味着该线程就能立即进入执行状态(即使是runnable状态也不一定是正在执行),而是对线程注册一些相关信息,并等待调用该线程实例的start方法来启动该线程进入runnable状态。
ii) Runnable (就绪状态)
当对该线程的实例调用start方法后,该线程进入runnable状态,进入runnable状态的线程并不意味着一定就在执行中(不然就叫running thread了),它可能被阻塞或者正在执行。即使处于正在执行中的Runnable thread,也不一定是持续着执行一直到结束,它很可能因为操作系统所分配的时间片到期而进入中断状态,而让其他获得时间片的线程执行,当其他的线程所占据的时间片到期后,将会根据所有等待执行的线程的优先级来确定哪个线程继续(或开始)执行,所以也未必是刚刚那个被抢占的线程恢复执行。
iii) Run(运行状态)
线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。
iv) Blocked (阻塞状态)
1.一个线程由于并执行命令sleep (DELAYS) 而进入睡眠状态。只有当设定的延时DELAYS到期后,该线程才能重新回到runnable状态。
2.一个线程在等待I/O操作的完成而进入阻塞状态。只有当相应的I/O操作完成之后,该线程才能回到runnable状态。
3.由于另一个线程目前处于锁定状态中,所以这个线程无法进入runnable状态而被阻塞。只有当那个处于锁定状态的线程让出了锁定权,那么这个线程(和其他的线程)才能进入runnable状态。(一旦一个线程被锁定,那么整个系统只能等待该线程执行完了之后才能执行别的线程。)
4.一个线程在等待某个条件的改变而进入阻塞状态。只有当另一个线程sign the condition may have changed之后,该线程会去检查这个条件是已改变,如果确实改变了,那么该线程才能进入runnable状态。
5.一个线程由于执行suspend方法而被挂起,所以进入阻塞状态。只有当该线程被执行resume方法后,才能回复runnable状态。suspend和resume方法已经逐渐不再使用。
v) Dead (死亡状态)
处于死亡或终止状态的线程将不再是可调度的,并且再也不会得到CPU时间,它的任务已经结束,或不再是可运行的。
i) 定义一个类继承Thread类,重载run方法,代码如下:
public class MyThread extends Thread { public void run(){ System.out.println("do the business method"); } public static void main(String[] args){ MyThread myThread = new MyThread(); myThread.start(); } } |
ii) 定义一个类实现Runnable接口,实现run方法,代码如下:
public class MyTask implements Runnable{ public void run(){ System.out.println("do the business method"); } public static void main(String[] args){ MyTask myTask = new MyTask(); Thread thread = new Thread(myTask); thread.start(); } } |
推荐使用此方法开启线程,这样不用去继承Thread类,可以继承其他父类,使得编码更为灵活。
使用executor建立线程池可以简化开发。
实现Callable接口,代码如下:
public class TaskWithResult implements Callable<String> { @Override public String call() throws Exception { return "result"; } public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService exec = Executors.newCachedThreadPool(); Future<String> future = exec.submit(new TaskWithResult()); System.out.println(future.get()); } } |
影响任务行为的一种简单方法是调用sleep(),这将使任务中止执行给定的时间。
线程的优先级将该线程的重要性传递给了调度器。尽管CPU处理现有线程集的顺序是不确定的,但是调度器将倾向于让优先权最高的线程先执行。然而,这并不是意味着优先权较低的线程将得不到执行(也就是说,优先权不会导致死锁)。优先级较低的线程仅仅是执行的频率较低。可以调用set Priority()来设置线程的优先权。
如果知道已经完成了在run()方法的循环的一次迭代过程中所需的工作,就可以给线程调度机制一个暗示:你的工作已经做得差不多了,可以让别的线程使用CPU了。这个暗示将通过调用yield () 方法来作出(不过这只是一个暗示,没有任何机制保证它将会被采纳)。当调用yield()时,你也是在建议具有相同优先级的其他线程可以运行。
所谓后台线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此,当多有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程。反过来说,只要有任何非后台线程还在运行,程序就不会终止。通过调用SetDaemon (true) 方法设置一个线程为后台线程。
一个线程可以在其他线程之上调用join()方法,其效果是等待一段时间直到第二个线程结束才继续执行。如果某个线程在另一个线程t上调用t.join(), 此线程将被挂起,直到目标线程t结束才恢复(即t.isAlive返回为假)。
Java以提供关键字synchronized的形式,为防止资源冲突提供了内置支持。 当任务要执行被synchronized关键字保护的代码片段的时候,它将检查锁是否可用,然后获取锁,执行代码,释放锁。
对象的锁
a) Synchronized方法
所有对象都自动含有单一的锁(也称为监视器)。当在对象上调用其任意Synchronized方法的时候,此对象都被加锁,这时该对象上的其他synchronized方法只有等到前一个方法调用完毕并释放了锁之后才能被调用。
b) Synchronized 同步控制块
有时,你只是希望防止多个线程同时访问方法内部的部分代码而不是防止访问整个方法。通过这种方式分离出来的代码段被称为临界区(Critical section),它也使用synchronized关键字建立。这里,synchronized被用来指定某个对象,此对象的锁被用来对花括号内的代码进行同步控制。
Synchronized (syncObject){
// this code can be accessed
// by only one task at a time
}
类的锁
针对每个类,也有一个锁,所以synchronized static方法可以在类的范围内防止对static数据的并发访问。
Wait () 使你可以等待某个条件发生变化,而改变这个条件超出了当前方法的控制能力。通常,这种条件将由另一个任务来改变。Wait () 在等待外部世界产生变化的时候将任务挂起,并且只有在notify() 或notify All ()发生时,这个任务才会被唤醒并去检查所产生的变化。调用wait()的时候将释放锁。
作用于Java本地函数的调用,如调用C++代码时,创建的内存,需要在finalize()用本地方法调用C++的内存释放函数。
一种简单但速度很慢的垃圾回收技术。每个对象都含有一个引用计数器,当有引用连接至对象时,引用计数器加1。当引用离开作用域或被置为null时,引用计数减1。垃圾回收器会遍历全部对象列表,当发现某个对象的引用计数为0时,就释放其占用的空间。这种方法有个缺陷,如果对象之间存在循环引用,可能会出现“对象应该被回收,但引用计数却不为零”的情况。定位这样存在交互引用的对象组所需的工作量极大。
对任何活的对象,一定能最终追溯到其存活在堆栈或静态存储区之中的引用。这个引用链条可能会穿过数个对象层次。由此,如果你从堆栈和静态存储区开始,遍历所有的引用,就能找到所有活的对象,然后是此对象包含的所有引用,如此反复进行,直到根源于堆栈或静态存储区的引用所形成的网络全部被访问为止。
在这种方式下的垃圾回收技术有:
A) 分代复制(复制式回收器)
先暂停程序的运行,然后将所有存活的对象从当前堆复制到另一个堆,没有被复制的全部都是垃圾。当对象被复制到新堆时,它们是一个挨着一个的,所以新堆保持紧凑排列,然后就可以直接地分配新空间了。
缺点:对于这种回收机制而言,有两个原因会降低效率。
1. 需要额外内存
你得有两个堆,然后你得在这两个分离的堆之间来回捣腾,从而得维护比实际需要多一倍的空间。某些Java虚拟机对此问题的处理方式是,按需从堆中分配几块较大的内存,复制动作发生在这些大块的内存之间。
2. 程序进入稳定状态后,可能只会产生少量垃圾,甚至没有垃圾,这时这种方式很浪费。
B) 标记清扫
从堆栈和静态存储区出发,遍历多有的引用,进而找出所有存活的对象。只有全部标记工作完成的时候,清除动作才会开始。在清除过程中,没有标记的对象将被释放,不会发生任何的复制动作。所以剩下的堆空间是不连续的,垃圾回收器要是希望得到连续空间的话,就得重新整理剩下的对象。
垃圾回收器工作时可以采取自适应的方式,智能地在“复制”与“标记清扫”两种机制上切换。
标签:
原文地址:http://www.cnblogs.com/tangyanbo/p/4306180.html