javaSE_3
来源:互联网 发布:unity3d 3dsmax 编辑:程序博客网 时间:2024/05/20 03:05
数据结构(对象容器):集合
习惯写法: Collection ee = new ArrayList() ;
java默认的equals和== 效果是相同的,判断是不是引用相同的对象 Collections工具类提供了很多静态方法(基于List容器),操作集合 sort() 排序 shuffle() 对容器内的对象进行随机排列 fill() 用一个特定对象重写整个List容器 .... Arrays工具类提供 操作数组 Comparable接口 实现了compare接口的类通过实现comparaTo方法从而确定此类对象的排序方式 选择数据结构: 衡量标准:读的效率和改的效率 Array读快改慢 Linked改快读慢 Hash两者之间 (效率很低,少用) (传智版) Collection的作用是存储数据,它的存储不是“持久化”的 (Deque)双端队列既是队列也是栈 Collection方法: 添加数据: add 删除数据: remove(Object o) 取出数据: 清空:clear() 判断是否包含指定的元素。contains(obj); 判断集合是否为空: isEmpty 获取集合里元素的数量:size() --下面这些方法是集合与集合进行运算-- DEMO addAll(Collection<? extends E> c) 集合的加法。 boolean removeAll(Collection<?> c) 集合的加法。 boolean retainAll(Collection<?> c) 集合的交集。 boolean containsAll(Collection<?> c) Set HashSet : 存储机制是简单地说,一个萝卜一个坑。 HasCode决定其存放在位置 HashSet的优势:存、取速度非常快。 觉得HashSet性能高低的是loadfactor(负载因子), 创建HashSet时,capacity越大,存取速度越快; 但capacity越大,内存开销就越大。 负载因子的作用:控制HashSet满到什么程度,就应该重新分配更多的坑。 负载因子越大,空间利用率高,节省内存;存取速度可能会下降。 负载因子越小,存取速度相当快。内存开销大。 对于Set来说,当两个元素通过equals()比较返回true时, Set会认为它们相等。 对于HashSet来说,一般要求其中的元素满足条件: 重写hashCode和equals方法: 当两个元素的hashCode相同时,它们通过equals比较最好也返回true。 TreeSet : DEMO 底层是“红黑树”实现。 TreeSet集合中元素是有序的,它的顺序是按照元素的大小排序的。 所以TreeSet要求集合中元素可以排序!否则就无法加到TreeSet中。 TreeSet有两种排序方式: 1. 自然排序。要求每个元素本身就是可比较大小的,它要求每个元素都实现Comparable接口。 2. 定制排序。由一个特定的对象来进行排序。特定的对象就是Compartor对象。 它要求创建TreeSet时,指定一个排序器,也就是Compartor对象。 TreeSet : 它的性能比不上HashSet。通常来说,它没有HashSet快。 它有一个好处:加入TreeSet中的元素总是处于有序状态(按大小排序)。 遍历数组的方法: for (iterator <String > it = c2.iterator() ; it.hasNext()) //这个好像效率好低 增强的for 循环 List : DEMO 比Collection多了一个功能,每个元对应的索引值, ArrayList 它的底层其实就是一个数组, 可以创建ArrayList指定它的数组初始长度,默认长度是10 缺点:当添加的元素的超过了ArrayList底层数组长度时,会新创建一个数组(长度好像是原来 的2倍),将老数组的元素复制到新数组中去,原来的被垃圾回收了, 在中间添删元素/时,群体搬家 优点:存取差不多相当于数组 linkedList 长度没有限制 遍历数组的接口: Enumeration接口 早期版本,不用了
Iterator接口
Map : DEMO
Map中每个元素都是 key-value对,key-value当成一个整体
实际上Map在存储元素集合时,value是不考虑的,
如果我们只考虑map里的key,所有的key就组成了一个set --keySet
所以keySet()返回所有key所组成的Set集
存放key-value对时,只考虑key部分,不会考虑value部分,
所以HashMap对key的要求,与HashSet对集合元素的要求是一模一样的,
HashSet对集合元素的要求:
合理的重写equals hashcode,如果两元素的HashCode相同,那么他们通过equals也应该是true
HashMap的元素也是有序的,按key来排序
Set和Map的关系:
从逻辑上来看,Map相当于是key中所有元素都增加一个value附属物,
从底层实现来看,Set是用Map实现的,Set相当于value都是new Object的Map对
List和Map的关系:
可以把List当作 所有key值都是int的Map
如果只看Map里的value,Map的value会组成普通Collection集合
所以Map提供了values方法来返回所有的value所组成的Collection的集合
与HashMap相似的有HashTable:
区别:
HashTable古老
HashTable性能不好,线程安全的
HashTable不允许键值对为null ,但HashMap允许的
Set中集合具有:元素不能重复,无序的特征
Map中key具有:key不能重复,无序的特征
HashCode作用:
高级 防止内存泄漏
(台湾版)
Stack接口 后进先出(last-in,first-out,LIFO)
Queue接口 先进先出(first in,first out,FIFO) 可认为是功能弱化的线性表(List)
Iterator - Collection - List/Set/Map
List接口 类似于数组, 循环有索引的链表结构 DEMO 有顺序,可以重复 线性表
Set 接口 没有顺序,不能重复
Map 接口 使用”键--值“(Key-Value)存取 不能有重复的键
Iterator接口 (所有实现Colleciton接口的容器都有一个iterator方法用以返回一个实现了Iterator接口的对象
iterator对象称作迭代器,用以方便的实现容器内元素的遍历操作)
for (iterator <String > it = c2.iterator() ; it.hasNext())
Iterable接口
ArrayStack类
ArrayQueue类
ArrayList类 DEMO1 使用Iterator类改写DEMO1
ArrayIterator类
LinkedStack类
LinkedQueue类
LinkedList类 StringStack StringStackDemo StringQueue StringQueueDemo
ListIterator类
HashSet类 HashSet的排序规则是利用哈希法(Hash),所以加入HashSet容器的对象还必须重新定 义hashCode()方法。HashSet根据哈希码来确定对象在容器中存储的位置,也可以根据 哈希码来快速地找到容器中的对象。
在先比较两个加入HashSet容器中的对象是否相同时,会先比较hashCode()方法返回的 值是否相同。如果相同,则再使用equals()方法比较,如果两者都相同,则视为相同的 对象。 HashSetDemo LinkedHashMapDemo
TreeSetDemo CustomComparator TreeSetDemo2
HashMap类 HashMapDemo HashMapDemo2 LinkedHashSetDemo
最常用集合框架: 总结 (清华版)
ArrayList类, 高级DEMO 加入相同的不会覆盖,但Map会覆盖的
Vector类, (古老的类,但是线程安全的)
Stack类
Map结构的集合类
Properties 类是Hashtalbe的子类
增加了将Hashtable对象中的关键字和值保存到文件和从文件中读取关键字和值到Hashtable对象中的方法;如果要对Properties.store方法存储Properties对象中的内容,每个属性的关键字和值都必须是String类型
DEMO 使用Properties把程序的启动运行次数记录在某个文件中,每次运行时打印出它的运行次数
Set结构的集合类
HashSet类,TreeSet类
Queue结构的集合类
Queue接口
HashMap hm = new HashMap();
可以写成
Map hm = new HashMap();
Vector类与Enumeration接口
Collection接口与Iterator接口
Collection、Set、List的区别
Set、List 是Collection接口的子类
Collection各元素对象之间没有指定的顺序,允许有重复元素的多个null元素对象
Set各元素对象之间没有指定的顺序,不允许有重复元素,最多允许有一个null元素对象
List各元素对象对象之间有指定顺序,允许有重复元素和多个null元素对象
基本数据类型 要转换成包装类才能add到集合类中去 (好像5.0后会自动转换)
基本数据类型 | 包装类 |
boolean | Boolean |
byte | Byte |
char | Character |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
HashMap和Hashtable的都是java的集合类,都可以用来存放java对象,但他们也有区别:
1.历史原因,Hashtable基于陈旧的Dictionary类,HashMap是java1.2引进的Map接口的一个实现。
2.同步性,Hashtable是线程同步的。保证了线程安全。而HashMap则是异步的,并不是线程安全,但速度会变慢。
3.可以让你将空值作为一个表的条目的key或value但是Hashtable是不能放入空值(null)的
(HashMap 类没有分类或者排序。它允许一个 null 键和多个 null 值。
Hashtable 类似于 HashMap,但是不允许 null 键和 null 值。它也比 HashMap 慢,因为它是同步的。 )
并发性(线程)
同一时刻只能有一个指令执行,但多个进程指令被快速轮换执行,使得宏观上具有多个进程同时执行的效果
并行性(进程)
同一时刻,有多条指令在多个处理器上同时执行
线程开销小,可以共享内存,线程之间进程数据交换比较容易
多线程 Thread):
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
Thread子类对象的start(从Thread类继承到的)方法,而此start方法将产生一个新的线程,并在此线程上运行此Thread类对象中的run方法。根据运行对象时的多态性,在此线程上实际运行的的是Thread子类(也就是我们写的那个类)对象中的run方法
前台线程
后台线程 setDaemon(true); (如果只有后台线程在运行,没有前台线程的话,全部线程就会结束)
当你需要把一条线程设为后台线程,必须先设置,再启动。
extends Thread创建一个TestThread线程类
TestThread tt = new TestThread();
tt.start();
tt.start();
...
只是产生一个线程
new TestThread().start();
new TestThread().start();
new TestThread().start();
产生3个线程
implements Runnable 创建一个TestThread线程类
TestThread tt = new TestThread();
new Thread(tt).start();
new Thread(tt).start();
new Thread(tt).start();
产生3个线程
try{Thread.sleep(1);} catch(Exception e){}
//线程停1毫秒
线程安全: synchronized(str ) {}
线程的同步是按照某一对象的标识位,,
同步代码块,同步方法 区别
同步方法使用的同步监视对象是this,如果要让同步代码块,同步方法同步,同步代码块就用this作为同步监视对象。
线程优先级: DEMO
线程的优先级用1-10之间的整数表示,数值越大优先级越高,默认的优先级为5。
可用getPriority()方法读取一个线程的优先级,并用setPriority()改变它。
运行该程序时,我们可注意到几件事情。首先,线程组的默认优先级是5。即使在启动线程之前(或者在创建线程之前,这要求对代码进行适当的修改)将最大优先 级降到5以下,每个线程都会有一个5的默认优先级。
试着提高组的最大优先级。可以发现,这样做是没有效果的。我们只能减少线程组的最大优先级,而不能增大它。 setPriority( Thread. NORM_PRIORITY + 2 )
) NORM_PRIORITY相当于5
c#线程优先级:
线程优先级: DEMO
线程的优先级可以定义为ThreadPriority枚举的值,即Highest、AboveNormal、Normal、BelowNormal和 Lowest。
创建线程的两个方式: 三种方式
继承java.lang.Thread类
实现java.lang. Runnable接口 灵活 (线程安全)
Eraser EraserDemo 基本上建议用此方式让对象具有纯程功能,保留日后修改的弹性
this.active = active;
前面两种种方式的线程执行体(也就是run方法)不能声明抛出异常也不能有返回值
第三种方法:实现Callable接口(它是Runnable的增强版)。 DEMO 好处是得到返回值
实现call()(run()的增强版)方法,call()方法就是线程执行流。
a. 创建Callable实现类的对象。
b. 将Callable实现类的对象包装FutureTask,它代表了call()方法的返回值将来才会返回。
c. 以FutureTask的对象作为target参数,创建Thread对象
d. 调用Thread对象的start()方法来启动它。
//调用FutureTask的get()方法时要小心!
千万不要直接调用线程执行体的方法!既不要调用run方法,也不要调用call方法。
实现Rannable和Callable本质上是相同的
Daemon线程 主线程结束后,Daemon线程就会跟着结束 DaemonThread
线程生命周期 创建线程(new Thread)、就绪(Runnable)、堵塞(Blocked)、消亡(Dead)
如果想让目前线程礼让一下其他线程,让它们有机会获得执行权,可以调用yield()方法[W1]
使用isAlive()方法来测试线程是否存活。
有几种状况会让线程进入Blocked状态:
等待输入输出完成
调用sleep()方法
尝试获得对象锁定
调用wait()方法
Thread类的静态方法:
currentThread():获取当前正在执行的线程。
sleep(n):让当前正在执行的线程暂停n ms,当前线程将会进入阻塞状态。
yield():让当前正在执行的线程暂停,它只是让当前线程进入就绪状态。
yield方法是给那些比当前线程优先级高、相等的线程一个执行的机会。
以下几个情况让线程回到Runnable状态:
输入输出完成
调用interrupt() InterruptDemo
获得对象锁定
调用notify()或notifyAll() [W2] Clerk Consumer Producer ProductTest
线程的加入(join)
当线程使用join()加入至另一个线程时,另一个线程会等待这个被加入的线程工作完毕,然后再继续它 的动作 ThreadA (变成单线程)
yield() 让出CPU
线程的停止 //让线程停止 最好用这种方法 DEMO
Thread的stop()方法已经被标示为deprecated,不建议使用stop()来停止一个线程的运行。
如果想要停止一个线程,最好自行实现。一个线程要进入Dead状态,就是执行完run()方法,简单地 说,如果您想要停止一个线程的执行,就要提供一个方式让线程可以完成run()的流程,而这也是自行 实现线程停止的基本概念。
如果线程的run()方法中执行的是一个重复执行的循环,可以提供一个标志(flag)来控制循环是否执行, 从而让循环有机会终止,使线程可以离开run()方法以终止线程: DEMO
如果线程因为执行sleep()而进入Blocked状态,而您想要停止它,可以使用interrupt(),而程序会抛出 InterruptedException异常,因而使得线程离开run()方法 SomeThread
如果程序因为等待输入输出设备而停滞(进入Blocked状态),比如说引发一 个异常,而这个异常要如何 引发,要看所使用的输入输出而定。
例如您使用readLine()等待网络上的一个信息,此时线程进入Blocked直到读到一个信息,您要让 它离开run()的方法就是使用close()关闭它 的字符串流,这时会引发一个IOException异常而使得线程 离开run()方法,例如:
上面这个程序是个简单的架构示范,实际的设计必须视您的程序功能与I/O类型而定。除了stop() 之外,suspend()、resume()方法也被标示为deprecated,如果要达成与这些方法相同的功能,都必 须自行设计。
线程组(ThreadGroup)
Thread.currentThread().getThreadGroup().getName(); [W3]
可以自行指定线程组,线程一旦归入某个组,就无法更换组。
ThreadGroup threadGroup1 = new ThreadGroup("group1");
ThreadGroup threadGroup2 = new ThreadGroup("group2");
Thread thread1 =
new Thread(threadGroup1, "group1's member");
Thread thread2 =
new Thread(threadGroup2, "group2's member");
ThreadGroup中的某些方法,可以对所有的线程产生作用,例如interrupt()方法可以interrupt线程组中所有的线程,setMaxPriority()方法可以设置线程组中线程所能拥有的最高优先权(本来就拥有更高优先权的线程不受影响)。
如果您想要一次获得线程组中所有的线程来进行某种操作,可以使用enumerate()方法,例如:
Thread[] threads = new Thread[threadGroup1.activeCount()];
threadGroup1.enumerate(threads);
activeCount()方法获得线程组中正在运行的线程数量,enumerate()方法要传入一个Thread数组,它将线 程对象设置到每个数组字段中,然后就可以通过数组索引来操作这些线程。
ThreadGroup中有一个uncaughtException()方法。当线程组中某个线程发生Unchecked exception异常时, 由执行环境调用此方法进行相关处理,如果有必要,您可以重新定义此方法,直接使用范例15.9来示 范如何实现。
线程池() (JDK 1。5之前要手动实现,) Executors工厂类
线程池在系统启动时即创建大量空闲的线程
程序将一个Runable对象传入线程池,线程池就会启动一条线程来执行此对象的run方法,当run方法执行结束后,此线程并不会死亡,而是再次回到线程池中成为空闲状态,等待执行下一个Runnable对象的run方法。
除此之外,使用线程池可以有效地控制系统中并发线程的数量,当系统中包含大量并发线程时,会导致系统性能急剧下降,甚至JVM崩溃,而线程池的最大线程的最大线程数参数可以控制系统中并发线程数目不超过过此数目。
//创建一个具有固定线程数的线程池
ExecutorService pool = Executors.newFixedThreadPool(6) ;
//向线程中提交线程 这时线程已经在执行了。
Pool.sumit(new TestThread()) ;
//关闭线程池
Pool.shutdown() ;
UncaughtExceptionHandler
在J2SE 5.0之前,如果要统一处理某些线程的Unchecked Exception,可以使用ThreadGroup来管理。 在继承ThreadGroup之后重新定义其uncaughtException()方法,就如范例15.9所示。在J2SE 5.0之后, 就不用这么麻烦,可以让异常处理类使用Thread.UncaughtExceptionHandler接口,并实现 其 uncaughtException()方法
ThreadExceptionHandler ThreadGroupDemo2
同步化(synchronized)
synchronized关键词可用于方法上,让方法所包括的范围(Scope)内都成为同步区域,
使其在同一时间只能有一个线程访问 例如:
public synchronized void setNameAndID(String name, String id) {this.name = name;this.id = id;if(!checkNameAndIDEqual()) {System.out.println(count +") illegal name or ID.....");}count++;}
synchronized的设置不仅可用于方法上,也可以用于限定某个程序区块为同步区域,例如:
public void setNameAndID(String name, String id) {synchronized(this) {this.name = name;this.id = id;if(!checkNameAndIDEqual()) {System.out.println(count +") illegal name or ID.....");}count++;}}
也可以标识某个对象要求同步化。
// arraylist参考至一个ArrayList的一个实例
synchronized(arraylist)
{
arraylist.add(new SomeClass());
}
同步代码块: 需要显式指定同步监视器。
同步方法: 不需要显式指定同步监视器。
当方法是实例方法时,同步监视器是this。
当方法是静态方法时,同步监视器是当前类。
用了synchorozied 之后,会导致性能下降。
用synchorozied 时要小心,它所控制的代码块应该尽量小!
synchorozied 应该恰好只控制修改共享资源的代码块。
DEMO synchronized(this) 当调用此方法时,锁定当前对象
死锁 DEMO
生产者消费者模式 :DEMO
模式运用到线程间通信
DMEO 不太准确
推荐用这种方法 更加简单的,把同步代码放到同一个类中 DEMO
Synchronized
区别太大
wait (object对象的方法) (wait 后 锁就不归原来所有了)
Wait时别的线程可以访问锁定的对象
调用wait方法时必需锁定此对象
sleep (Thread对象的方法)
sleep时别的对象不可以访问锁定对象
notify 叫醒一个在我这个对象上wait的对象,让它继续执行
notify/notifyAll
沿学版 生产者消费者问题 ProducerConsumer.java
如果您要暂停线程,但暂停的时间未知,使用sleep()并不是个好方法,您可以使用wait()让线程进入Blocked状态,
然后让别的线程用notify()或notifyAll()来通知被Blocked的线程回到Runnable状态
通过wait()方法您可以要求线程进入对象的等待池(Wait Pool),或是通知线程回到锁定池的Blocked状态。
必须在同步的方法或区块中才能调用wait()方法(也就是线程获得锁定时)。当对象的wait()方法被调用,目前的线程会被放入对象的等待池中,线程归还对象的锁定,其他的线程可竞争对象的锁定;被放在等待池中的线程也是处于Blocked状态,所以不参与线程的调度。
wait()可以指定等待的时间。如果指定时间的话,则时间到了之后,线程会再度回到锁定池的Blocked状态,等待竞争对象锁定的机会,如果指定时间为0或不指定,则线程会持续等待,直到被中断(interrupt),或是被告知(notify)回到锁定池的Blocked状态
获得目前线程所属的线程组名称:
线程间的通信 : java所有对象都有这些方法
wait:告诉当前线程放弃监视器并进入睡眠状态直到其他线程进入同一个监视器并调用notify为止
notify:唤醒同一对象监视器中调用wait的第一个线程。用于类似饭馆有一个空位后通知所有等候就餐的顾客中的第一位可以入座的情况
notify All:唤醒同一对象监视器中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行。用于类似某个培训班终于招满人,通知所有学员都来上课的情况
容器类的线程安全(Thread-safe)
容器类默认没有考虑线程安全问题,您必须自行实现同步以确保共享数据在多线程存取下不会出错。例如,若您使用 List对象时,可以这样实现:
// arraylist参考至一个ArrayList的一个范例 |
事实上,您也可以使用java.util.Collections的synchronizedXXX()等方法来传回一个同步化的容器对象,例如传回一个同步化的List:
List list = Collections.synchronizedList(new ArrayList()); |
以这种方式返回的List对象,在存取数据时会进行同步化的工作。不过在您使用Iterator遍访对象时,仍必须实现同步化,因为这样的List使用iterator()方法返回的Iterator对象,并没有保证线程安全(Thread-safe),一个实现遍历的例子如下:
List list = Collections.synchronizedList(new ArrayList()); |
在J2SE 5.0之后,新增了一个package,即java.util.concurrent。其中包括了一些确保线程安全的Collection类,例如ConcurrentHashMap、CopyOnWriteArrayList、CopyOnWriteArraySet等。这些新增的 Collection 类的基本操作与先前介绍的Map、List、Set等对象是相同的,所不同的是增加了同步化的功能,而且依对象存取时的需求不同而有不同的同步化实现,以同时确保效率与安全性。
例如,ConcurrentHashMap针对Hash Table中不同的区段(Segment)进行同步化,而不是对整个对象进行同步化。ConcurrentHashMap默认有16个区段,当有线程存取第一个区段时,第一个区段进入同步化,然而另一个线程仍可以存取第一个区段以外的其他区段,而不用等待第一个线程存取完成,所以与同步化整个对象来说,新增的ConcurrentHashMap、CopyOnWriteArrayList、CopyOnWriteArraySet等类,在效率与安全性上获得了较好的平衡。
Vector
Hashtable
---------------------------------
即使我们需要使用线程安全的集合,也没必要使用Vector和Hashtable。
我们可以另一个工具类:Collections,
它里面提供了系列的 synchronizedXxx,它用于将原有的集合包装成线程安全的集合。
HashMap map = new HashMap(); //线程不安全的。
HashMap map = Collections.synchronizedMap(new HashMap()); //线程安全的。
ThreadLocal类
http://book.51cto.com/art/200812/103988.htm
为每个线程创造一个资源的复本。将每一个线程存取数据的行为加以隔离,实现的方法就是给予每个线程一个特定空间来保管该线程所独享的资源
concurrent套件新增类
BlockingQueue
http://book.51cto.com/art/200812/103989.htm
Callable与Future
http://book.51cto.com/art/200812/103994.htm
Executors
http://book.51cto.com/art/200812/103995.htm
(疯狂JAVA版)
JVM进程的结束:
1. 程序运行完成。
2. 程序执行System.exit(); Runtime.getRuntime().exit()
3. 遇到未捕获的异常。
4. 所在平台(操作系统)强行结束进程。
类加载:
类加载:指的是将该类对应.class文件(通常在磁盘、可能直接在内存中、可能来自于网络)
读入内存,并创建对应的Class对象。
1. 加载。将二进制文件(class文件,通常在磁盘上)读入内存。
2. 连接。为类生成对应的Class对象(堆内存的permanent代中),并将它存入JVM中。[W1]
3. 初始化。对静态属性进行初始化,并调用静态初始化块对类执行初始化。[W2]
(对于一个final型的静态属性,如果此属性可以在编译时就得到属性值,则可认为此属性可被当居编译时常量。当程序使用编译时常量时,系统会认为这是对此类的被动使用,所以不会导致此类的初始化)DEMO
(当使用classLoader类的loadClass()方法来加载某个类时,此方法只是加载此类,并不会执行此类的初始化。当使用Class的forName()静态方法的才会导致强制[初始化此类)
String类
Java中所有的类,都是Class对象。
所有类、每个类 → 有个对应Class对象,它就在内存的Permanent代。
所有类的加载都由一个叫 ClassLoader的东西来完成。
类加载机制:
3大特征:
全盘负责:当一个类加载器去加载某个类,该类加载器将会同时加载与之关联的所有的类。
父类委托:当你要加载某个类时,总是先让父类加载器尝试加载; (委托机制)
只有当父类加载器无法加载该类时,才会由子类加载器去加载。
缓存机制:一个类被加载之后,将一直缓存在堆内存permanent代中。
Java的类加载器: DEMO [W3] 类加载器也是一个JAVA类
大部分时候,系统提供了4个类加载器。
根加载器 (在程序中一般不需要访问 jre/lib/rt.jar)
↑
扩展类加载器 (会负责加载JDK系统的类库 jre/lib/ext/*.jar)
↑
系统类加载器 (该类加载器会负责加载CLASSPATH中的class文件。它会负责加载程序员写的类)
↑
用户类加载器
private(类访问) → <default>(包访问)→ protected(子类访问)→ public(全局)
自定义类加载器的方法: DEMO
1. 继承ClassLoader抽象类。
2. 重写关键方法findClass即可。
findClass(String name)
loadClass(String name)
核心方法
defineClass(String name, byte[] b, int off, int len):通常不应该考虑重写该方法。
将b数组中,从off位置开始、长度为len的字节内容转换为Class对象。
一般考虑重写findClass方法。
通过反射查看类信息:
如何获取类对应的Class对象。(字节码)
- 调用Class.forName(类名字符串); Class qq = Class.forName("org.crazyit.test.Hello")
- 调用指定类的class属性。 Class qq = org.crazyit.test.Hello.class
- 调用指定对象的getClass()方法。 Class qq = "aasas".getClass()
一个虚拟机,对于一个类,最多只初始化一次。
一个类,在一个虚拟机里,最多只有一个对应的Class对象。
确定一个方法需要3个方面。
1 哪个类。 由Class对象确定。
2 方法名
3 形参列表
确定一个构造器需要2个方面:
1. 哪个类 由Class对象确定。
2. 形参列表。
确定一个Field需要2个方面:
1. 哪个类 由Class对象确定。
2. Field名字
Class对象的方法: DEMO
getConstructor/getConstructors 用于获取构造器。
getMethod/getMethods用于获取方法。
getField/getFields用于获取属性。
getSuperclass获取父类
getInterfaces获取所实现的接口。
Class对象的作用:用于获取类内部的其他成分(方法、构造器、field) DEMO
获取了Constructor,可用于产生对象。
获取了Method之后,可以调用方法。
获取了Field之后,可以访问、或者修改它的值。
反射会导致 程序性能下降
Field类 成员变量的类
Method类
使用BeanUtils工具包
利用到logging工具包
反射就是加载类《〈 解剖出类的信息
java里有一个Class类,代表某个类的字节码
Class.forName()
类名.class()
对象.getClass()
Class对象提供了如下常用方法:
Public Constructor getConstructor(Class<?>... parameterTypes)
Public Method getMethod(String name, Class<?>... parameterTypes)
Public Field getField(String name) public
public Constructor getDeclaredConstructor(Class... parameterTypes)
public Method getDeclaredMethod(String name,Class... parameterTypes)
public Field getDeclaredField(String name)
//反射类无参的构造方法
Class clazz = Class.forName("cn.itcast.reflect.Person");
Constructor c = clazz.getConstructor(null);
Object obj = c.newInstance(null);
System.out.println(obj);
//反射类有参的构造方法:public Person(String name)
Class clazz = Class.forName("cn.itcast.reflect.Person");
Constructor c = clazz.getConstructor(String.class);
Person p = (Person) c.newInstance("flx");
System.out.println(p);
私有成员外界不能访问,,但反射可以做到
//反射类私有的、有参的构造方法:private Person(int name)
Class clazz = Class.forName("cn.itcast.reflect.Person");
Constructor c = clazz.getDeclaredConstructor(int.class);
c.setAccessible(true);//暴力反射
Person p = (Person) c.newInstance(1);
System.out.println(p);
利用Method执行方法:
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),
利用Field访问属性
public void set(Object obj,Object value)
public Object get(Object obj)
利用反射做框架:
BufferedReader br = new BufferedReader(new FileReader("src/config.txt"));
String className = br.readLine();
String methodName = br.readLine();
Class clazz = Class.forName(className);
Method method = clazz.getMethod(methodName, null);
method.invoke(clazz.newInstance(), null)
内省(Introspector)
开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以sun公司开发了一套API,专门用于操作java对象的属性。
private String name ; //这时仅仅是一个字段
如果name有get 或set方法,name才算是一个属性
PropertyDescriptor pd = new PropertyDescriptor("name",Student.class);
Method method = pd.getWriteMethod(); //setName()
method.invoke(s, "flx");
//System.out.println(s.getName());
method = pd.getReadMethod(); // getName
String result = (String) method.invoke(s, null);
System.out.println(result);
通过PropertyDescriptor类操作Bean的属性
通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法。
//操作bean的所有属性
BeanInfo info = Introspector.getBeanInfo(Student.class);
PropertyDescriptor pds[] = info.getPropertyDescriptors();
for(PropertyDescriptor pd : pds){
System.out.println(pd.getName());
}
beanutils工具包
Beanutils工具包的常用类:
BeanUtils BeanUtils.setProperty(s, "name", name);
PropertyUtils
ConvertUtils.regsiter(Converter convert, Class clazz) 类型转换器
自定义转换器
根类加载器并没有继承ClassLoader抽象类,所以扩展类加载器的getParent()方法返回null,但实际上根加载器确实是扩展类加载器的父类加载器
反 射:(台湾版) (内省 IntroSpector)
可以通过Object的getClass()方法来取得每个对象对应的class对象..ClassDemo
也可以使用以下方法取得String类的Class类对象
Class stringClass = String.class
声明参考名称并不会加载类,而使用new生成对象时才会加载类 TestClass LoadClassTest
数组也是一个对象,也有其对应的Class实例 ClassDemo2
类加载后,JVM就会自动为其生成一个Class对象
使用Class.forName()加载类
forNamer的第二个版本,指是否运行静态区块,指定类加载器
TestClass2 ForNameDemoV1 ForNameDemoV2
从Class中获取信息
Class对象表示所加载的类,取得Class对象之后,就可以取得与类相关联的信息, 像包、构 造函数、方法成员等,而每一个信息也会有相应的类类型。
例如包的对应类型是java.lang.Package ClassInfoDemo
构造函数的对应类型是java.lang.reflect.Constructor SimpleClassViewer
方法成员的对应类型是java.lang.reflect.Method InvokeMethodDemo
域成员的对应类型是java.lang.reflect.Field
类加载器
类加载器以java.lang.classLoader类型存在
合作行执行java xxx.class指令后,java运行程序会找到JRE安装的所在目录,然后寻找jvm.dll (默认在JRE目录下的bin/clint目录下),接着启动JVM并进行初始化动作,产生Bootstarp Loader, Bootstarp Loader会加载Extended Loader,并设置Extended Loader的parent为Bootstarp Loader, Bootstarp Loader会加载System Loader,并将System Loade的parent设置为Bootstarp Loader[W1]
最后System Loader开始加载指定的类。在加载类时,每个类加载器会先将加载类的任务交给其 parent,如果parent找不到,再由自己负责加载。
所以会以Bootstarp Loader -- Extended Loader -- System Loader 的顺序来寻找类,都找不到就会传 出NoClassDefFoundError。
Bootstarp Loader会搜索系统参数sun.boot.class.path中指定位置的类,默认是JRE所在目录 的classes下的.class文件或lib目录下的.jar文件中(如rt.jar)的类并加载。
可以使用System.getProperty("sun.boot.class.path")语句来显示指定的路径
Extended Loader会搜索系统参数java.ext.dirs中指定位置的类,默认是JRE目录下的 lib/ext/classes目录下的.class文件lib目录下的.jar文件中(如rt.jar)的类并加载。
可以使用System.getProperty("java.ext.dirs")语句来显示指定的路径
System Loader会搜索系统参数java.class.path中指定位置的类,也就是classpath所指定的 路径。
可以使用System.getProperty("java.class.path")语句来显示指定的路径
在使用java运行程序时,也可以加上-cp来覆盖原有的Classpath设置
Java -cp ./classes someclass
每个类被加载后都会有一个class的实例来代表,而每个Class的实例都会记得自己是由哪个 ClassLoader加载的。
可以由class的getClassLoader()取得加载此类的ClassLoader,而从ClassLoader的getParent()方 法可以取得自己的parent。 DEMO
[W2] (下面这个有点复杂,必要时是不用理的)
getParent() getParent() getClassLoader() getClass() Bootstrap Loader ExtClassLoader AppClassLoader Class someclass 实例 someclass
取得ClassLoader的实例之后,可以使用它的LoadClass来加载类。
使用LoadClass加载类时,不会运行静态区块 ForNameDemoV3
ExtClassLoader与AppClassLoader都是java.net.URLClassLoader的子类,可以在使用java启动 程序时,使用以下指令来指定ExtClassLoader的搜索路径:
Java -Djava.ext.dirs=c:/workspace/YourClass
使用-classpath或-cp来指定AppClassLoader的搜索路径,也就是设置Classpath:[W3]
Java -classpath c:/workspace/YourClass
使用自己的ClassLoader
打算动态地从其他路径加载类,就要产生新的类加载器
在新增了ClassLoader实例后,可以使用它的loadClass()方法来指定要加载的类名称。
在新增ClassLoader时,会自动将新建的ClassLoader的parent设置为AppClassLoader,并在每 次加载时,先委托parent代为搜索。所以上例搜索someclass类时,会一直往上委托至Bootstrap Loader开始搜索,接着是ExtClassLoader、AppClassLoader。如果都找不到,才使用新建的 ClassLoader搜索
所以,java的类加载层次架构:动态加载类,安全
由同一个ClassLoader加载的类文件,会只有一份Class实例。如果同一个类文件由两个不 同 ClassLoader载入,则会有两个不同的class实例。
注意,如果有两个不同的ClassLoader搜索同一个类,而在parent的AppClassLoader搜索路径中可以找到指定类,则CLass实例就只会有一个,因为两个不同的ClassLoader都是在委托父ClassLoader时找到此类的。如果父ClassLoader找不到,而是由各自的ClassLoader搜索到,则class的实例会有两份 ClassLoaderDemo
使用反射机制生成与操作对象:
使用反射机制可以在运行时期,动态加载类并生成对象,操作对象上的方法,改变类成员的值,甚至连私有成员的值也可以改变。
生成对象:
newInstance()实例化一个对象,实例化的对象以Object类型返回 NewInstanceDemo
调用方法:
调用student类的setName方法InvokeMethodDemo
修改成员值:
数组对象 ArrayDemo NewArrayDemo NewArrayDemo2
Proxy对象
要在类中的一个方法的调用前后加上记录的功能,但又不会将记录的功能写到类中,使用Proxy类来实现动态代理
使用Proxy.newProxyInstance()建立一个代理对象,建立对象时必须告知所要代理的接口,然后可以操作所建立的代理对象,在每次操作时会调用InvocationHandler的invoke()方法
IHello HelloSpeaker LogHandler ProxyDemo
javadoc注释:
javadoc 标记由“@”及其后所跟的标记类型和专用注释引用组成
javadoc 标记有如下一些:
@author 标明开发该类模块的作者
@version 标明该类模块的版本
@see 参考转向,也就是相关主题
@param 对方法中某参数的说明
@return 对方法返回值的说明
@exception 对方法可能抛出的异常进行说明
@author 作者名
@version 版本号
其中,@author 可以多次使用,以指明多个作者,生成的文档中每个作者之间使用逗号 (,) 隔开。@version 也可以使用多次,只有第一次有效
使用 @param、@return 和 @exception 说明方法
这三个标记都是只用于方法的。@param 描述方法的参数,@return 描述方法的返回值,@exception 描述方法可能抛出的异常。它们的句法如下:
@param 参数名 参数说明
@return 返回值说明
@exception 异常类名 说明
javadoc -d doc -subpackages com.liigo
其中 com.liigo 是主包最终生成的API文档位于 c:/src/doc 目录
Annotation: 注释 注解
可以在包、类、方法、域成员等加上Annotation
@Override 必须是重写父类中的同名方法 CustomClass CustomClass2
@Deprecated 使用一段时间后,不建议开发人员使用 Something SomethingDemo
@SuppressWarnings 忽略本来的警告信息 SomeClass SomeClass2
UnitTest FunctionTest UnitTest2
Meta-annotation
告诉编译器如何处理 annotation @Retention:
在使用Retention类型时,需要提供 java.lang.annotation.RetentionPolicy的枚举类型
Package java.lang.annotation;
Public enum RetentionPolicy{
SOURCE, //编译器处理完annotation 信息后就没事
CLASS, //编译器将annotation 存储于CLass文件中,默认
RUNTIME //编译器将annotation 存储于CLass文件中,可由VM读入
}
SomeAnnotation SomeClass3 AnalysisApp
限定annotation 使用对象@Target:
在定义Annotation类型时,使用java.lang.annotation.Target可以定义其适用的时机。在定义时要指定要 指定java.lang.annotation.ElementType的枚举值之一:
Package java.lang.annotation;
Public enum ElementType{
TYPE, //适用class,interface,enum
FIELD, //适用field
METHOD, //适用method
PARAMETER, //适用method上之parameter
CONSTRUCTOR, //适用constructor
COCAL_VARIABLE, //适用区域变量
ANNOTATION_TYPE, //适用annotation类型
PACKAGE //适用package
}
要求为API文件的一部分@Documented TwoAnnotation
继承父类的annotation @Inherited ThreeAnnotation
默认父类中的Annotation并不会被继承至子类中。可以定义Annotation类型时加上 java.lang.annotation.Inherited类型的Annotation,继承保留于子类中。
日志(Logging): 使用Logging工具类
Logging的等级:LoggingLevelDemo
Logger默认的处理者是(Handler)是java.util.logging.ConsolerHander,也就是将信息输出至命令模式。一个 Logger可以有多个处理者,每个处理者可以有自己的信息等级,在通过Logger的等级限制后,实际上还要 再经过处理者的等级限制。要看到所有的信息,如:LoggingLevelDemo2 另一种写法LoggingLevelDemo3
Handler和Formatter
Logger默认的输入处理者是ConsolerHanler。ConsolerHanler的输出是使用System.err对象,而信息的 默认等级是INFO,这可以在JRE安装目录下lib目录的logging.properties中看到:
Handlers = java.util.logging.consoleHandler
java.util.logging.ConsoleHandler.level = INFO
Java提供了五个默认的Handler:
java.util.logging.ConsoleHandler 以System.err输出日志
Java.util.logging.FileHandler 将信息输出至文件 HandlerDemo
Java.util.logging.StreamHandler 以指定的OutputStream实例输出日志
Java.util.logging.SocketHandler 将信息通过Socket传送至远程主机
Java.util.logging.MemoryHandler 将信息暂存在内存中
自定义Formatter格式:
FilHandler默认输出格式是XML格式,输出格式由java.util.logging.Formatter来控制。例如FilHandler 的默认格式是java.util.logging.XMLFormatter,而ConsoleHandler的默认格式是 java.util.logging.SimpleFormatter。可以使用Handler实例的setFormatter()方法来设置信息的输出格式。
例如: fileHandler.setFormatter(new SimpleFormatter());
TableFormatter TableFormatterDemo
Logger层次关系: LoggerHierarchyDemo
信息绑定:更改文字信息时,只要更改文本文件的内容而不用重新编译程序
使用ResourceBundle:
格式化信息:
国际化信息:messages3_zh_cn I18NDemo
JDBC入门: CRUD Create(创建)、Read(读取)、Update(更新)和Delete(删除)
JDBC数据库驱动程序按实现方式可以分为4个类型:
Type 1:JDBC-ODBC Bridge
Type 2:Native-API Bridge
Type 3:JDBC-middleware
Type 4:Pure Java Driver
连接数据库
加载JDBC驱动程序 [W4]
提供JDBC URL
协定:子协定:数据源识别
jdbc:mysql://主机名称:连接端口/数据库名称?参数=值&参数=值
jdbc:mysql://localhost:3306/demo?user=root&password=123
如果要使用中文存取的话,还必须给定参数useUnicode及characterEncoding,表明是否使用Unicode,并指定字符编码方式,例如:
jdbc:mysql://localhost:3306/demo?user=root&password=123&useUnicode=true&characterEncoding=Big5
获得Connection对象之后,可以使用isClosed()方法测试与数据库的连接是否关闭,在操作完数据库之后,如果确定不再需要连接,则必须使用close()来关闭与数据库的连接,以释放连接时相关的必要资源。
简单的Connection工具类
先设计一个DBSource接口,规范获得Connection的方法 DBSource
实现DBSource接口,您的目的是从属性文件中读取设置信息、加载JDBC驱动程序 SimpleDBSource
使用一个简单的程序来测试SimpleDBSource ConnectionDemo