您的位置:首页 > 编程语言 > Java开发

Java笔记

2015-08-30 21:56 381 查看
与C++不同,Java不支持多重继承。即Java一个类只能有一个直接父类。
1.关键字
访问修饰关键字:public protected private
类、方法、变量修饰关键字:class interface implement extends abstract static new
流控制关键字:if-else switch-case-default for while do-while break continue return instanceof
异常处理关键字:try-catch-catch-finally throw throws assert
包控制关键字:import(将包导入到程序中) package(用于定义包,并将类定义到包中)
数据类型关键字:boolean char byte short int long flort double
特殊类型和方法关键字:super(用于引用父类) this(用于引用当前对象)

2.一些与C++不太相同的定义
定义一个常量 final double PI=3.14;
字符串相加 string s="lg"+"lk"; s=s+30; //字符可以与数字相加
<< 左移运算符
>> 右移运算符(如果为负数,最高位填充1)
>>> 无符号右移运算符(最高位始终填充0)

3.{} 块控制符号,决定了变量的使用范围
4.数组
Java的数组是一种特殊的对象
声明数组:int array[];或 int[] array;//但是没有分配内存还不能使用
array=new int[5];//为数组分配内存
int[] array={1,2,3,4};//直接初始化

string[]用于接收命令行参数: public static void main(String args[])
数组的拷贝属于浅拷贝: int a[]={1,2,3};int[] b={4,5,6}; a=b;//则a,b指向同一个内存空间{4,5,6} ;可以使用system.arraycopy()进行数组的深度拷贝
Java中的多维数组又一维数组组成,所以支持不规则数组。
int[][]twod=new int[4][];
twod[0]=new int[1];
twod[1]=new int[4];
twod[2]=new int[5];
twod[3]=new int[7];
for-each语句: int[] intarray={1,2,3};for(int i:intarray){...}

5.java函数的入参是引用的浅拷贝(入参没有深度拷贝)。

6.Java的包
包通过目录树来管理源代码,使得当前.Java源代码文件位于哪一个层级下。

7.继承
与C++不同,Java中默认变量和函数的属性是包内继承访问为public,包外继承访问为private(C++默认为private)
与C++不同,Java中重载、覆盖、多态的实现中,子类和父类都不需要任何关键字(C++实现多态需要virtual)
final class CM{...} :指明该类不能被再被继承
final void func(..){} :指明子类中不能再复写基类中声明为final的函数
final int var;:指明该变量为常量

8.abstract类和abstract方法(除了方法没有实现外,没有其它特殊的用途)
类中没有实现的方法必须声明为abstract方法,含有abstract方法的类必须声明为abstract类
继承abstract类的子类一定要实现所有的abstract方法,否则该子类也应该声明为abstract类
abstract类中可以包含已经实现的函数。

9.Object类
Object类是所有类的基类,如果Java中某一个类没有继承任何类,那么它默认继承了Object类。
于是Object类可以存储任何类的引用。
Object obj=new Fish();
((Fish)obj).eat();//类型提升

object类中实现了boolean equals(Object obj){return this==obj}方法,判断2个引用的是否指向同一个对象。该方法可以重载
if (obj instanceof Fish){...} 判断Object指针是否保存的是Fish类型的对象

10.Interface(纯抽象类)
与C++不同,Java不支持多重继承。即Java一个类只能有一个直接父类。所有JAVA解决复杂问题显得力不从心。Interface类可以解决这个多重继承问题。
一个类可以继承多个接口,继承了接口的类必须实现接口中的所有方法。
*Interface中可以声明变量,但被自动设置为public static final属性
*Interface中的方法全部是abstract的
*Interface修饰符可以没有货为public。“没有”表明该接口只能在该报中使用;public表明任何类都可以使用该接口。
*不能有构造函数

public interface animal{
int bit_type=5; //==public static final int bit_type=5;
void sleep();
void eat(); //== abstract void eat();
}

接口的实现:
class 类名 implements 接口1,接口2{
方法1(){..}
方法2(){..}
}

11.内部类
*内部类定义在一个类的内部,只能被宿主类使用。于是可以很好的控制类的可见性。
*内部类分为2中:静态内部类和非静态内部类。非静态内部类可以访问宿主类的所有变量和方法;静态内部类中不能直接问宿主成员,可以隔离宿主类(如果在内部类中需要可以构造宿主对象访问)
*局部内部类:定义在方法中的类。
*匿名内部类

12.对象克隆(深度复制)
*Java色深度复制可以继承接口Clonable,并实现 public Object clone(){}方法,在该方法中可以调用object类的super.clone()实现。
super.clone()也只能复制当前继承关系类中的基本类型的变量,对当前类中的组合形式的复制对象(自定义类然后再该类中实例化的对象)也只是浅复制(复制引用,因为组合对象在该类中保存的就是引用)

13.异常
Java中所有的异常都是内置类Throwable的子类,所以Throwable在异常类型层次结构的顶部。Throwable有2个子类,形成了2个不同的分支Exception和Error。

try{
class.forname("");
}catch(ClassNotFoundException e){
system.out.println("将异常再次抛出");
throw e;
}finally{
close...
}
14.Java容器
Java容器和C++容器一样分成2中:顺序型和关联型,其派生结构如下:



(1)关联型。
ArrayList: 相当于C++的Vector。采用数组实现,并且实现了可变长度的数组,允许保存所有对象的元素,包括NULL。可以根据索引位置对集合进行快捷的随机访问。缺点是向指定的索引位置插入对象或删除对象的速度较慢。 语法 List<string>lt=new ArrayList<string>();
LinkedList: 相当于C++的List。该类采用链表结构保存对象。便于向集合中插入和删除对象,但是对于随机访问速度较慢。语法 List<string>lt=new LinkedList<string>();
Set有2种实现hash和tree。只是实现算法不同,访问都是相同的。set中的对象是无序的,遍历Set集合的结果与插入Set集合的顺序并不相同。语法 Set<string>lt=new HashSet<string>();
(2)关联型
Map有2种实现hash和tree。2种方式对于不同的数据,效率可能不同。
Map map=new HashMap();
Emp emp1=new Emp("001","张三");
Emp emp2=new Emp("003","张1");
Emp emp3=new Emp("003","张5");
map.put(emp1.getE_id(),emp1.getE_name());
map.put(emp2.getE_id(),emp2.getE_name());
map.put(emp3.getE_id(),emp3.getE_name());

Set set=map.keySet();
Iterator it=set.iterator();//指向第一个元素之前,it.next()访问一次指针加1,并返回next对象引用
while(it.hasNext())
{
String str=(String)it.next();
String name=(String)map.get(str);
system.out.println(str+" " +name);
}
}
15.字节流访问和字符流访问
首先数据的保存分成2种:字节流形式和字符流形式。以字节流形式保存的数据是二进制数据,以字符流形式保存的数据是文本数据。所以对数据的存取也因保存方式不同而不同。
(1)字节流访问
字节流类提供了数据源(文件、网络、压缩包、管道、缓存、其它)到目的地(指定内存)字节流形式的存取方法。(不同的字节流提供的是对不同数据底层来源的读写方法不同)。根据对于数据的读入还是写入分为输入流和输出流InputAtream/OutputStream,






InputAtream/OutputStream自带一定量缓存,使得以缓存为数据源/数据目的地的类可以以这些类为底层。如DataOutputString和BufferedOutputStream可以以FileOutStream为底层。作者应该根据不同的底层如文件还是管道选择不同的流类进行存取。

(2)字符流访问
字符流类提供了数据源(文件、网络、压缩包、管道、缓存、其它)到目的地(指定内存)字符(一个字符2个字节)形式的存取方法。(不同的字符流提供的是对不同数据底层来源的读写方法不同)。根据对于数据的读入还是写入分为输入流和输出流Reader/Writer,Reader/Writer自带一定量缓存。(Reader/ Writer 的底层实际也是使用InputAtream/OutputStream来实现的。)






一个读文件字符流的例子:
char[] temp=new char[1024];
StringBuilder sb=StringBuilder(); //相当于一个string,但比string更强大
FileReader reader=new FileReader("D:/lj.txt");
while(reader.read(temp) !=-1)
{sb.append(temp)}
(3)文件类
File类提供了对文件和路径的所有操作方式。File类是io包中唯一代表磁盘文件本身的对象,实现文件或路径的创建、删除、重命名、权限等操作。
File lf=new File("D:/lm.txe");//创建一个文件
if(lf.exists()){lf.delete()}
else{lf.createNewFile();}
16.thread
(1)创建线程的方法
Java中有2中方法创建线程:一种是对Thread类进行派生并覆盖run方法;一种是通过实现runnable接口创建。
第一种:继承Thread类创建线程
Thread类的run方法是空的,直接创建thread对象不能完成任何事,所以必须继承Thread类并重写run方法。
class Threaddemo extends Thread{
Threaddemo(){}
Threaddemo(string szname){
super(szname);
}
public void run()//重载run方法
{
while(true)
{...}
}
public static void main(string argc[])
{
Threaddemo td1=new Threaddemo();
td1.start();
Threaddemo td2=new Threaddemo();
td2.start();
}
}

第二种:Runnable接口创建线程

class Threaddemo implements Runnable{
public void run()//重载run方法
{
while(true)
{...}
}
public static void main(string argc[])
{
Runnable rb1=new Threaddemo();
Thread td1=new Thread(rb1);
td1.start();
Runnable rb2=new Threaddemo();
Thread td2=new Thread(rb2);
td2.start();
}
}
(2)线程周期控制:
Thread类封装了对线程周期的控制函数
.start() 启动一个线程
.stop()结束一个线程
.sleep(long)暂停long-ms
.suspend()挂起执行
.resume()恢复执行
.yield()明确放弃执行
.wait()进入阻塞状态
.notify()使一个线程阻塞状态解除

.isAlive() 判断线程是否结束,非阻塞
.join()等待线程结束,阻塞

(3)线程调度
.setPriority() 设置优先级
.getPriority获取优先级

(4)线程同步-同步代码块synchornized
当把一个语句块声明为synchornized,所有的线程将互斥访问该代码块。
有2中形式:
声明一个函数为需要互斥的函数
public synchornized funcname(){...} //则run中调用该函数将是互斥的
声明一个代码块或变量需要互斥
synchornized(obj){...} obj可以写任意对象,可以用this代替都行,把需要互斥的代码写在{}中

(5)线程通信
Java中的线程通信通过wait() notify() notifyall()等完成,可以解决生产者/消费者模型

17.synchronized同步访问
  多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多个线程同时访问时可能出现的问题。同步机制可以使用synchronized关键字实现。当synchronized方法执行完或发生异常时,会自动释放锁。
synchronized实现同步的要点是:主要观察synchronized锁住的哪个对象和哪些方法会竞争该对象。
synchronized能实现3种同步:
(1)synchronized修饰一个类中的一个或多个普通方法(this对象锁:同一对象中的synchronized方法互斥)
synchronized会先锁住了该this对象,然后再执行代码,该this对象的其他synchronized方法此时如果想锁住该this对象将会被阻塞。不影响非synchronized方法,因为非synchronized不会企图去锁住该this对象。这其中又可以分为多个线程中,该对象的同一synchronised方法竞争this锁或不同synchronised方法竞争this锁,原理都一样。
(2)synchronized修饰一个类中的一个或多个static方法(类锁:不同对象中的synchronized static方法互斥)
synchronized会先锁住了该类,然后再执行该静态方法,该类的其他synchronized static方法此时如果想锁住该类将会被阻塞。不影响非synchronized方法,因为非synchronized不会企图去锁住该类。这其中又可以分为多个线程中,同一synchronised static方法竞争类锁或不同synchronised static方法竞争类锁,原理都一样。
(3)synchronized修饰一个块(任意对象锁:对该范围的访问需要去竞争同一指定对象)
块同步用处最大,可以用来实现上述2中同步。该方法竞争的这个对象可以是跨方法跨类的,从而形成多线程中同一对象同一方法之间、不同方法之间和不同对象的方法之间的互斥。
18.java中的匿名内部类
因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口。匿名内部类是通过覆盖来实现的。
实例1:匿名内部类的基本实现
abstract class Person {
public abstract void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
运行结果:eat something
可以看到,我们直接将抽象类Person中的方法在大括号中实现了这样便可以省略一个类的书写并且,匿名内部类还能用于接口上
实例2:在接口上使用匿名内部类
interface Person {
public void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
运行结果:eat something
由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口
实例3:Thread类的匿名内部类实现
public class Demo {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
t.start();
}
}
运行结果:1 2 3 4 5
实例4:Runnable接口的匿名内部类实现
public class Demo {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
Thread t = new Thread(r);
t.start();
}
}
运行结果:1 2 3 4 5

下面是《java从入门到精通》的一点点笔记
java.lang.*
1.接口
[修饰符] interface 接口名 [extends 父接口名列表] {
[public] [static] [final] 变量;
[public] [abstract] 方法; //只定义不能实现;abstract可以写也可以不写,都是虚方法

接口实现使用implements 关键字

例子:
public interface IimageSaver{
void save();
}
public class GIFSaver implements IimageSaver{
@override //可有可无,可以在编译时检测该方法是否已经存在,多态和覆盖都可以使用该关键字
public void save(){
System.out.println("将图片保存为GIF格式");
}
}
2.继承与覆盖
[修饰符] class 子类名 extends 父类名{
类体

覆盖:子类含有与父类相同的方法(形参也相同),且不是abstract方法
public class Animal{
public Animal(){}
public void cry(){
System.out.println("动物发出叫声");
}
}
public class Dog extends Animal{
public Dog(){}
public void cry(){
System.out.println("狗发出叫声");
}
}

public class zoo{
public static void main(String[] args){
Dog dog =new Dog();
dog.cry();
}
}
3.super
有2种作用:用以调用父类的构造方法super([参数列表]);用以引用父类被覆盖的变量或方法super.父成员变量名,super.父成员方法([参数列表])

4.多态及抽象类
多态分2种:普通方法重载和抽象方法覆盖。
public abstract class shape{
public string getName(){
return this.getclass().getSimpleName();
}
public abstract double getArea(); //抽象类中含有抽象方法,抽象方法在定义时不能被实现
}

5.内部类
内部类分4种:成员内部类、局部内部类、匿名内部类、静态内部类
a.成员内部类:外部类的成员方法和成员变量对成员内部类都是可见的;内部类只能在外部类中声明和实例化(因为对象需要绑定到外部类对象)。
成员内部类中不能声明静态成员。
public class OuterClass{
innerClass in=new innerClass();
public void out(){
in.inf();
}
public class innerClass{
innerClass(){}
public void inf(){}
int y=0;
}
public innerClass doit(){
in.y=4;
return new innerClass();
}
public static void main(String args[]){
outerClass out=new outerClass();
outClass.innerClass in=out.doit(); //这里返回的内部类对象是外部类对象的一部分。
outClass.innerClass in1=new out.innerClass(); //内部类必须是与外部对象绑定的
}
}

使用类名+this关键字访问被覆盖的成员
public class TheSameName{
private int x;
private class inner{
private int x=9;
public void doit(int x){
x++;//访问当前的x
this.x++;//访问当前类成员x
TheSameName.this.x++;//访问指定类对象的成员x
}
}
}
b.局部内部类
局部内部类是指在类的方法中定义的内部类,它的作用范围(被构造和析构)也只能在这个方法体内。
外部方法及外部类的变量和方法对局部内部类都是可见的。
public class SellOutClass{
private String name;
public SellOutClass(){name="apple";}
public void sell(int price){
class Apple{
int innerPrice=0;
public Apple(int price){
innerPrice=price-10;
}
public void price(){
System.out.println("now on sell"+name);
System.out.println("price is:"+innerPrice);
}
}
Apple apple=new Apple(price);
app.price();
}
public static void main(String[] args){
SellOutClass sample=new SellOutClass();
sample.sell(100);
}
}

c.匿名内部类
语法格式为: return new A(){...}; A表示对象名
匿名内部类一般用于实现抽象类或接口类
interface Person {
public void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}

d.静态内部类
在成员内部类前添加static,就变成了静态成员内部类。静态成员内部类不能访问外部类的非静态成员。
静态成员内部类有2个特点:1)创建静态内部类的对象,不需要其外部类的对象;2)不能从静态内部类的对象中访问非静态外部类的对象。
public class staticinnerclass{
static int x=10;
static class Inner{
static void doitInner(){
system.out.println("外部成员的static变量为:"+x);
}

public static void main(String args[]){ //位于静态内部类中
doitInner();
}
}
}
上述代码将编译生成一个名称为staticinnerclass$Inner的完全独立类和一个staticinnerclass类,只需要使用java staticinnerclass$Inner命令就可以运行
main方法中的内容,这样当测试完成需要将所有的.class文件大包时,只需要删除staticinnerclass$Inner独立类即可。(否则需要为每一个类写一个main方法测试,测试完成后删除main方法,然后重新编译)

6.class类与反射
Java的反射功能通过class类和Object类来完成,java.lang.reflect包提供了对该功能的支持。
Object对象的getClass()方法可以返回一个与该Object对象对象绑定的class对象。
通过反射可以获得和调用某个对象中的所有成员。
7.异常处理
a.try-catch-finally
try{
需要执行的语句
}catch(Exception ex){
异常时执行的语句
}finally{
一定会被处理的语句


b.throws 声明该方法中可能存在的异常,并要求在调用该方法的地方处理异常(方法中可以不抛出异常)
格式:
数据类型 方法名(形参列表) throws 异常类1,异常类2...异常类n{
方法体
}
例子:
public void showInfo() throws Exception{//该方法可能存在异常
FileInputStream in=new FileInputStream("C:/Record.txt");
}

void methodName(){//调用showInfo()的地方一定要处理可能的异常
try{
showInfo();
}catch(Exception ex){
system.out.println(ex.getMessage());
ex.printStackTrace();
}
}
c.throw 抛出异常

throw new Exception("对异常的描述");

19.NIO(java.nio.*)
1.NIO
a.NIO的作用
常规IO以字节流和字符流stream为操作基础,而NIO以channel为操作基础,用以替代常规IO.channel中的数据只能与buffer或另外的channel的交换。Selector用于监听多个通道的事件(比如:连接打开,数据到达),实现IO多路复用。因此,单个线程可以监听多个数据通道。
channel底层还是依赖于流,channel对象需要通过stream对象来获得
FileOutputStream os = new FileOutputStream("src/scattingAndGather.txt");
FileChannel channel = os.getChannel();
b.为什么说NIO以channel为中心?
channel是NIO数据传输的动力,channel的.write(),.read(),.transferto(),.transferfrom()才能启动数据的传输;就像stream的.read()和write()方法一样.
channel和buffer的分离程度很大,只是channel的.write(),.read()方法只接收buffer类型的对象,channel将数据读到buffer之后,这些数据的操作就与channel没有关系了.(channel读出的数据可以放在另一个channel中或buffer或MappedByteBuffer中)
c.channel与stream模型的区别:
NIO和传统IO(一下简称IO)之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
常规IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入;NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel),NIO的强大功能来自于Channel的非阻塞特性。
d.选择阻塞的io还是非阻塞的nio?
正如他们的特性决定的,io时阻塞的,只有需要读取的数据根据要求被准备好了read/write才能返回;而NIO的非阻塞特性决定了需要用户在buffer中自己去判断需要的数据是否符合要求.
举例:从屏幕或者文件中读一行到内存中来
IO:只有读到完整的一行才会返回,而且会根据'\n'来判断是否是完整的一行
NIO:无法判断是否是完整的一行,有数据就读,直到buffer满为止;需要用户在buffer中自己判断数据的边界
结论:有边界的数据通过io来操作比较方便;而没有边界的数据,比如socket数据,通过NIO来操作比较方便.
2.NIO 3大组件
(1).Channel通道
Channel和IO中的Stream(流)是差不多一个等级的。只不过Stream是单向的,譬如:InputStream, OutputStream.而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。
NIO中的Channel的主要实现有:
FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel
这里看名字就可以猜出个所以然来:分别可以对应文件IO、UDP和TCP(Server和Client)。
(2).Buffer(各种数据类型的缓存)
buffer(替代常规IO的字节数组byte[])
NIO中的关键Buffer实现有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer,分别对应基本数据类型: byte, char, double, float, int, long, short。当然NIO中还有MappedByteBuffer, HeapByteBuffer, DirectByteBuffer等这里先不进行陈述。
buffer的2个重要io函数:
.flip()   让position指向可读数据开头,limit指向可读数据尾  (所以[写后]读前需要调用.flip())
.compact() 让position指向可写的buffer区域开头,limit指向可写的区域尾,在此limit通常=compacity.(所以[读后]写前需要调用.compact())
.rewind() 将position设置为0,所以可以重读Buffer中的从position到limit之间的数据。
(3)Selector
Selector运行单线程处理多个Channel,如果你的应用打开了多个通道,但每个连接的流量都很低,使用Selector就会很方便。例如在一个聊天服务器中。要使用Selector, 得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新的连接进来、数据接收等。
(4)其他功能
a.channel实现内存映射:xxxbuffer可以是使用MappedByteBuffer替代,从而将channel外的数据直接映射到MappedByteBuffer里面,可以像操作数组一样操作MappedByteBuffer,避免了一次数据拷贝.

RandomAccessFile aFile = new RandomAccessFile("src/1.ppt","rw");
FileChannel fc = aFile.getChannel();
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, aFile.length());
// System.out.println((char)mbb.get((int)(aFile.length()/2-1)));
// System.out.println((char)mbb.get((int)(aFile.length()/2)));
//System.out.println((char)mbb.get((int)(aFile.length()/2)+1));

b.数据分散与聚合Scatter/Gatter
分散(scatter)从Channel中读取是指在读操作时将读取的数据写入多个buffer中。因此,Channel将从Channel中读取的数据“分散(scatter)”到多个Buffer中。
聚集(gather)写入Channel是指在写操作时将多个buffer的数据写入同一个Channel,因此,Channel 将多个Buffer中的数据“聚集(gather)”后发送到Channel。
scatter / gather经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的buffer中,这样你可以方便的处理消息头和消息体。
ByteBuffer header = ByteBuffer.allocate(10);
ByteBuffer body = ByteBuffer.allocate(10);

byte [] b1 = {'0', '1'};
byte [] b2 = {'2', '3'};
header.put(b1);
body.put(b2);
ByteBuffer [] buffs = {header, body};
FileOutputStream os = new FileOutputStream("src/scattingAndGather.txt");
FileChannel channel = os.getChannel();
channel.write(buffs);   //channel可write()方法可以接收一个buffer数组

c.读管道与写管道的对接,省略了buffer的过程
RandomAccessFile fromFile = new RandomAccessFile("src/fromFile.xml","rw");
FileChannel fromChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("src/toFile.txt","rw");
FileChannel toChannel = toFile.getChannel();

toChannel.transferFrom(fromChannel,0, fromChannel.size());
//fromChannel.transferTo(0, fromChannel.size(),toChannel);
toFile.close();
fromChannel.close();

d.单向Pipe(一个线程读,另一个线程写)
Java NIO 管道是2个线程之间的单向数据连接。Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取。

Pipe pipe = Pipe.open();
ExecutorService exec = Executors.newFixedThreadPool(2);
final Pipe pipeTemp = pipe;
exec.submit(new Callable<Object>(){
@Override
public Object call() throws Exception
{
Pipe.SinkChannel sinkChannel = pipeTemp.sink();//向通道中写数据
while(true){
TimeUnit.SECONDS.sleep(1);
String newData = "Pipe Test At Time "+System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()){
sinkChannel.write(buf);
}
}
}
});

exec.submit(new Callable<Object>(){
@Override
public Object call() throws Exception
{
Pipe.SourceChannel sourceChannel = pipeTemp.source();//向通道中读数据
while(true){
TimeUnit.SECONDS.sleep(1);
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.clear();
int bytesRead = sourceChannel.read(buf);
System.out.println("bytesRead="+bytesRead);
while(bytesRead >0 ){
buf.flip();
byte b[] = new byte[bytesRead];
int i=0;
while(buf.hasRemaining()){
b[i]=buf.get();
System.out.printf("%X",b[i]);
i++;
}
String s = new String(b);
System.out.println("=================||"+s);
bytesRead = sourceChannel.read(buf);
}
}
}
});

e.DatagramChannel数据包通道
Java NIO中的DatagramChannel是一个能收发UDP包的通道。因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。

20.google库guawa中的eventbus

EventBus是Guava的事件处理机制,是设计模式中的观察者模式(生产/消费者编程模型)的优雅实现。可以实现类的实践订阅,当消息发布时,根据订阅顺序依次去执行类中的方法.有点像代理,但是比代理更强大.
public class TestEvent {//任意普通类:用于作为消息被传递
private final int message;
public TestEvent(int message) {
this.message = message;
System.out.println("event message:"+message);
}
public int getMessage() {
return message;
}
}
public class EventListener {  //任意普通类的任意方法,可以订阅消息
public int lastMessage = 0;

@Subscribe
public void listen(TestEvent event) {
lastMessage = event.getMessage();
System.out.println("Message:"+lastMessage);
}
public int getLastMessage() {
return lastMessage;
}
}
public class TestEventBus {
static public void main(args[] ) throws Exception {

EventBus eventBus = new EventBus("test");
EventListener listener = new EventListener();

eventBus.register(listener);

eventBus.post(new TestEvent(200));
eventBus.post(new TestEvent(300));
eventBus.post(new TestEvent(400));

System.out.println("LastMessage:"+listener.getLastMessage());
}
}
//输出信息
event message:200
Message:200
event message:300
Message:300
event message:400
Message:400
LastMessage:400

21.java反射机制(spring框架很好的诠释了反射的作用)
反射定义:主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
反射缺点:使用反射将打破封装性,使得java对象的属性不安全.
反射的作用: 通过反射机制访问java对象的属性,方法,构造方法等
反射包含的4个类:
java.lang.Class;
java.lang.reflect.Constructor; java.lang.reflect.Field;
java.lang.reflect.Method;
java.lang.reflect.Modifier;
反射具体功能实现:
a.反射机制获取类class对象有三种方法:(分别是通过class方法,类和对象都可以获得特定类的class对象)
Class c1 = Class.forName("Employee");
Class c2 = Employee.class; //java中每个类型都有class 属性.
Employeee = new Employee(); //java语言中任何一个java对象都有getClass 方法
Class c3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)
b.创建对象:获取类class对象之后,我们可以创建它的指向的对象,利用newInstance:
Class c =Class.forName("Employee");
Object o = c.newInstance(); //调用了Employee的无参数构造方法.
c.获取类的属性:分为所有的属性和指定的属性
Class c = Class.forName("User"); //获取类
Field idF = c.getDeclaredField("id");//获取id属性
//Field[] fs = c.getDeclaredFields();
Object o = c.newInstance();//实例化这个类赋给o
idF.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。//打破封装
idF.set(o, "110"); //set //给o对象的id属性赋值"110"
System.out.println(idF.get(o)); //get

d.获取方法,和构造方法
getDeclaredMethods()获取所有的方法
getReturnType()获得方法的返回类型
getParameterTypes()获得方法的传入参数类型
getDeclaredMethod("方法名",参数类型.class,……)获得特定的方法
getDeclaredConstructors()获取所有的构造方法
getDeclaredConstructor(参数类型.class,……)获取特定的构造方法
getSuperclass()获取某类的父类
getInterfaces()获取某类实现的接口

22.java并发编程(java.util.concurrent)
java1.5以后提供了该并发编程包,主要包含以下5方面的内容:
1.线程管理
a.线程创建的2种方法:Thread继承及Runnable接口实现
b.线程的一些属性获取与设置
Thread.currentThread().getName()
th.setPriority(...)
th.setName(...)
th.interrupt() 中断线程,将线程从阻塞等待中唤醒;当th.interrupt()被调用后,th的run()方法中将会抛出InterruptedException异常,可以通过捕获处理该异常来控制线程的中断.
TimeUnit.SECONDS.sleep(...)线程休眠
th.join() 等待线程终止
th.setDaemon(true) 设置线程为后台守护线程
c.java默认所有的线程都是前台前程,只有所有前台线程结束后,程序才会退出
d.ThreadLocal线程本地存储,通常被申明为以下形式:
class mythread extends Thread{
static ThreadLocal<classA> a=new ThreadLocal<classA>();
public void run(){...;a...;...}
}
e.线程组ThreadGroup:线程组提供对同一组线程的统一管理(例如同时被中断,同时启动...)
thgp.activeCount() 获取存活线程个数
thgp.currentThread():取得当前线程。
thgp.list() 打印线程组信息
thgp.enumerate(Thread[]) 返回所有存活线程
thgp.interrupt() 中断线程组中所有线程
thgp.getMaxPriority(); //获得当前线程组中最大优先级
thgp.resume(); //使被挂起的当前组内的线程恢复到可运行状态
thgp.setDaemon (boolean daemon); //指定一个线程为当前线程组的监护线程
thgp.setMaxPriority(int pri); //设置当前线程组允许的最大优先级
thgp.stop();//终止当前线程组中所有线程
thgp.suspend(); //挂起当前线程组中所有线程
...
使用方法:
ThreadGroup threadGroup = new ThreadGroup("groupname");
for (int i = 0; i < 5; i++) {
Thread thred = new Thread(threadGroup, new Runnable(){...});
thred.start();
}
//Thread[] threads = new Thread[threadGroup.activeCount()];
//threadGroup.enumerate(threads);
while (threadGroup.activeCount() < 2) {
threadGroup.interrupt(); //结束所有运行
}
2.线程同步(synchronized和Lock接口)及同步辅助类
a.synchronized锁住对象object,然后在该对象上调用wait(),notify(),notifyall()方法,实现生产者-消费者模型
b.Lock的实现:ReentrantLock可重入锁,ReentrantReadWriteLock;Lock是用于替代sychronized,一般来说比sychronized具有更高的效率;java 8有增加了stampedLock,比ReadWriteLock锁具有更高的效率.
c.Lock的fair模式和Non-Fair模式.在Non-Fair模式下,当有很多线程在阻塞等待同一个锁,该锁将没有约束的(任意)选择一个线程获得该锁;而在fair模式模式则会选择等待时间最长的线程获得该锁.默认情况下是Non-Fair模式.
Lock lock=new ReentrantLock(true);
d.在Lock与Condition配合使用,发挥条件变量的功能.
Lock lock=new ReentrantLock(true);
Condition cd= lock.newCondition();
cd.await();cd.signal();cd.signalAll();
一个任务队列通常需要1个Lock和2个条件变量,分别用于判断队列的满和空.
e.semaphore(permits)信号池 http://www.cnblogs.com/alipayhutu/archive/2012/05/25/2518620.html) permits:表示在不调用release前所能调用acquire而不被阻塞的次数。用于具有一定数量的共享资源的访问.(不要求.release()的线程一定要先.acquire();如果释放的比获取的信号量还多,例如获取了2个,释放了5次,那么当前信号量就动态的增加为5了;如果semaphore被初始化为4次,那么此时semaphore相当于被初始化为7次。这是permits的动态增加特性)。semaphore的动态上限特性很有用。这可以将permits初始化为0或为负值。
.acqure方法:当semaphore池中semaphore数量<=0时,被阻塞,一次只获取一个
.release(num)方法:总是能让池中信号数加1或加num,永远不会被不阻塞
.availablePermits() 返回池中semaphore数量
.drainPermits() 与acqure不同,drainPermits将非阻塞获取池中所有semaphore,返回获得个数
.tryAcquire(int permits)获取指定数量的semaphore,只有池中有充足的semaphore时,才会返回true
semaphore单向阻塞特性:acqure减到0或为负时阻塞;release加到permits上限时,将动态改变permits上限值而不会阻塞。
所以有时需要向上向下都需要阻塞时,就需要考虑使用2个semaphore。
permits动态增加特性:semaphore的上限可以动态增加。semaphore池中的semaphore数量被release增加了,从而permits被动态增加了。也就是说当前可以调用acquire而不被阻塞的次数=初始permits+已经调用的release次数-已经调用的acquire次数。
permits动态减少特性:semaphore的上限也可以动态较少。可以理解为semaphore池中的semaphore数量被动态减少了。这个特性靠只调用acqure而不release视乎就可以实现,但是错了,那样并没有改变permits的值。信号量本来有这个api的,不过是protected方法,所以我们需要显式继续Semaphore,并重新实现该api,见Semaphore类中的reducePermits(int reduction);从而将permits上限减小。
semaphore用法:如果需要先acqure,则可以将permits设置为>0的数;如果需要先release则可以将permits设置为等于或者小于0的数
例子:使用semaphore实现具有容量上限的阻塞队列。
public class Apple {
private String name;
public Apple(String name){
this. name= name;
}
@Override
public String toString() {
return name ;
}
}
public class Basket {
private List bascket =new ArrayList(10);
Semaphore mutex = new Semaphore(1);
Semaphore isFull = new Semaphore(10);
Semaphore isEmpty = new Semaphore(0);
public void put(Apple app) throws InterruptedException{
//acquire,就是减操作,如果小于等于0,就阻塞
//release,就是加操作,如果大于0,就不会被阻塞
isFull. acquire();
try{
mutex. acquire();
bascket.add( app);
}
finally{
mutex.release();
isEmpty.release();
}
}
public Apple take() throws InterruptedException{
Apple app;
isEmpty. acquire();
try{
mutex. acquire();
app= bascket.remove(0);
}
finally{
mutex.release();
isFull.release();
}
return app ;
}
}
f.CountDownLatch(倒数闸门:不可重用)允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行;阻塞在cdl.await()上的所有线程只有在cdl计数(门栓)为0时才能解除阻塞.可以通过cdl.countDown()将计数减一,直到让cdl.await()上的线程解除阻塞.用途:替代join;让多个线程同时运行;...
g.CyclicBarrier(循环栅栏:Cyclic表示可以重复使用):多个线程调用cb.await()阻塞在cb上并达到cb指定的数量时,这些线程才能同时被解除阻塞;同时在cb上可以挂载一个线程,当阻塞被解除时,该线程会运行.栅栏与计数门栓的功能类似.
CyclicBarrier barrier = new CyclicBarrier(3,new Runnable(void run(){...}););
new BillTask(new BillServiceImpl(), barrier, "北京").start();
new BillTask(new BillServiceImpl(), barrier, "上海").start();
new BillTask(new BillServiceImpl(), barrier, "广西").start();
class BillTask extends Thread {
private BillService billService; // 计费服务
private CyclicBarrier barrier;
private String code; // 代码,按省代码分类,各省数据库独立。
BillTask(BillService billService, CyclicBarrier barrier, String code) {
this.barrier = barrier;
this.code = code;
}
public void run() {
System.out.println("开始计算--" + code + "省--数据!");
...
System.out.println(code + "省已经计算完成,并通知汇总Service!");
barrier.await(); // 通知barrier已经完成
}
}
h.Phaser(阶段阻塞器):将并发任务分成多个阶段运行,只有当前阶段的所有线程全部执行完成才能开始下一阶段.Phaser是CountDownLatch的升级版,当线程只包含一个阶段时,Phaser等同于CountDownLatch用法.
Phaser的阶段是通过ph.arriveAndAwaitAdvance()来使当前线程完成本阶段的任务,arriveAndAwaitAdvance()数量到达ph指定线程数加ph.register()线程数时,此时ph.onAdvance()回调方法被调用,如果ph.onAdvance()返回true,则所有线程解除阻塞继续执行,并且ph.phase阶段属性自动加1;否则将使得ph.isTerminated()为true,线程可以根据此标志来退出执行,表示所有阶段执行完成.
适用于:一个任务分多个不同阶段并且每个阶段由多个线程共同来执行的情况.
本例要实现的功能为:开启3个线程,分别打印字母a,b,c各10次,然后进入下一阶段打印后面的字母d,e,f各10次,然后再进入下一阶段.......以此类推,直到整个字母表全部打印完毕。在此期间主程序进入等待状态,直到3个工作线程全都结束,主程序才能结束.
public class MyTest {
public static void main(String[] args) {
Phaser phaser = new Phaser(3) {//共有3个工作线程,因此在构造函数中赋值为3
@Override
protected boolean onAdvance(int phase, int registeredParties) {
System.out.println("\n=========华丽的分割线=============");
return registeredParties == 1; //本例中,当只剩一个线程时,这个线程必定是主线程,返回true表示终结
}};
System.out.println("程序开始执行");
for(int i=0; i<3; i++) { //创建并启动3个线程
new MyThread((char)(97+i), phaser).start();
}
phaser.register(); //将主线程动态增加到phaser中,此句执行后phaser共管理4个线程
while(!phaser.isTerminated()) {//只要phaser不终结,主线程就循环等待
int n = phaser.arriveAndAwaitAdvance();
}
//跳出上面循环后,意味着phaser终结,即3个工作线程已经结束
System.out.println("程序结束");
}
}
class MyThread extends Thread {
private char c;
private Phaser phaser;
public MyThread(char c, Phaser phaser) {
this.c = c;
this.phaser = phaser;
}
@Override
public void run() {
while(!phaser.isTerminated()) {
for(int i=0; i<10; i++) { //将当前字母打印10次
System.out.print(c + " ");
}
//打印完当前字母后,将其更新为其后第三个字母,例如b更新为e,用于下一阶段打印
c = (char) (c+3);
if(c>'z') {
//如果超出了字母z,则在phaser中动态减少一个线程,并退出循环结束本线程
//当3个工作线程都执行此语句后,phaser中就只剩一个主线程了
phaser.arriveAndDeregister();
break;
}else {
//反之,等待其他线程到达阶段终点,再一起进入下一个阶段
phaser.arriveAndAwaitAdvance();
}
}
}
}
i.Exchanger(数据交换器:可以是任何类型的数据):Exchanger可以在两个线程之间交换数据,只能是2个线程,他不支持更多的线程之间互换数据。当线程A调用Exchange对象的exchange()方法后,他会陷入阻塞状态,直到线程B也调用了exchange()方法,然后以线程安全的方式交换数据,之后线程A和B继续运行.
public class ThreadLocalTest {
public static void main(String[] args) {
Exchanger<List<Integer>> exchanger = new Exchanger<>();
new Consumer(exchanger).start();
new Producer(exchanger).start();
}

}
class Producer extends Thread {
List<Integer> list = new ArrayList<>();
Exchanger<List<Integer>> exchanger = null;
public Producer(Exchanger<List<Integer>> exchanger) {
super();
this.exchanger = exchanger;
}
@Override
public void run() {
Random rand = new Random();
for(int i=0; i<10; i++) {
list.clear();
list.add(rand.nextInt(10000));
list.add(rand.nextInt(10000));
list = exchanger.exchange(list);
}
}
}
class Consumer extends Thread {
List<Integer> list = new ArrayList<>();
Exchanger<List<Integer>> exchanger = null;
public Consumer(Exchanger<List<Integer>> exchanger) {
super();
this.exchanger = exchanger;
}
@Override
public void run() {
for(int i=0; i<10; i++) {
list = exchanger.exchange(list);
System.out.print(list.get(0)+", ");
System.out.print(list.get(1)+", ");
}
}
}
3.Concurrent Collections(并发集合)及Atomic Variables(原子变量):包含了一组支持并行处理的数据结构,大大简化了并行编程难度,减少了synchronized的使用.并发集合包括2种:同步阻塞式和同步非阻塞式并发集合:
a.阻塞式:当队列为满或为空时,读/写将被阻塞,直到可写或可读(BlockingQueue接口,BlockingDeque接口)
ArrayBolockingQueue,LinkedBlockingDeque,LinkedTransferQueue,PriorityBlockingQueue,DelayQueue,Vector(synchronized)、Hashtable(synchronized)
b.非阻塞式:当队列为满或为空时,读或写将返回空或抛出异常(ConcurrentMap接口)
concurrentLinkedDeque,concurrentSkipListMap,ConcurrentHashMap
CopyOnWriteArrayList,
一些方法:
.getFirst(),.getLast():返回队列中第一个和最后一个元素,不会删除元素,没有就抛出NoSuchElementException
.peekFirst(),.peekLast():没有就就返回NULL,不会删除元素
.remove(),.removeFirst(),.removeLast(),返回队列中第一个和最后一个元素,并删除元素,没有就抛出NoSuchElementException
c.原子变量:AtomicLong,AtomicIntegerArray
4.Executors(与线程组不同,Executors是线程池管理机制):
将线程管理委托给执行器,执行器将执行线程的创建,运行,管理并返回任务结果,节省计算机资源;延迟或周期性的执行任务;其它特点:Executors是基于线程池的,效率高;可以接收runnable和callable任务,当使用callable方法时将返回future对象;
4.1.线程与任务
在java中线程指thread创建的对象,在线程中可以执行任务,线程对象提供对线程的管理方法;而任务指Runnable和Callable接口实现的对象。
4.2.Executors主要接口介绍:
a.Executor:此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。此接口只有一个execute(Runnable command)方法
b.ExecutorService:ExecuteService继承了Executor,其中的submit方法对Executor的execute方法进行了扩展。
ScheduledExecutorService:ScheduledExecutorService继承了ExecuteService,可安排在给定的延迟后运行或定期执行的命令
c.ThreadPoolExecutor:BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(); ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.DAYS, queue);
线程池创建时需要传入一个阻塞队列,用于作为队列,缓存runnable和callable接口的任务。
一般都使用for循环将线程添加到Executor中去.

4.3.Executors主要static方法介绍:这些方法返回的都是ExecutorService对象,这个对象可以理解为就是一个线程池。
a.newFixedThreadPool(固定大小线程池)
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。可以根据系统资源设置线程数Runtime.getRuntime().availableProcessors()
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
b.newCachedThreadPool(无界线程池,可以进行自动线程回收)
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调 用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有60秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(index);
}
});
}
}
}
线程池为无限大,当执行第二个任务时,如果第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
c.newSingleThreadExecutor(单个后台线程)
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线 程,那么如果需要,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的newFixedThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定队列中的顺序(FIFO, LIFO, 优先级)执行。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
d.newScheduledThreadPool 可以对线程池的中的任务进行周期或延时管理。
创建一个定长线程池,支持定时及周期性任务执行,延迟执行。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
Runnable task=new Runnable() {
public void run() {
System.out.println("delay 3 seconds");
}
};
scheduledThreadPool.schedule(task, 3, TimeUnit.SECONDS); //表示延迟3秒执行。
//scheduledThreadPool.schedule(task, 1,10, TimeUnit.SECONDS); //表示延迟1秒后每10秒执行一次。
}
}

5.Fork/Join框架(分解/合并框架):提供了线程操作的支持。Fork/Join框架是在ExecutorService接口上实现的.
6.定制并发类:根据需要来改编java并发API中一些有用的类

23.java的cach机制
EHCache 的特点,是一个纯Java ,过程中(也可以理解成插入式)缓存实现,单独安装Ehcache ,需把ehcache-X.X.jar 和相关类库方到classpath中。如项目已安装了Hibernate ,则不需要做什么。直接可以使用Ehcache.
Cache 存储方式 :内存或磁盘

package test;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

public class Test {

public static void main(String[] args) {
//指定ehcache.xml的位置
String fileName="E:\\1008\\workspace\\ehcachetest\\ehcache.xml";
CacheManager manager = new CacheManager(fileName); //非单例->CacheManager.create(...)
//取出所有的cacheName
String names[] = manager.getCacheNames();
for(int i=0;i<names.length;i++){
System.out.println(names[i]);
}
//根据cacheName生成一个Cache对象
//第一种方式:
Cache cache=manager.getCache(names[0]);

//第二种方式,ehcache里必须有defaultCache存在,"test"可以换成任何值
// Cache cache = new Cache("test", 1, true, false, 5, 2);
// manager.addCache(cache);

//向Cache对象里添加Element元素,Element元素有key,value键值对组成
cache.put(new Element("key1","values1"));
Element element = cache.get("key1");

System.out.println(element.getValue());
Object obj = element.getObjectValue();
System.out.println((String)obj);
manager.shutdown();
}
}
Ehcache特点:
a.Ehcache 1.2具备对象API接口和可序列化API接口。
b.不能序列化的对象可以使用除磁盘存储外Ehcache的所有功能。
c.支持基于Cache和基于Element的过期策略,每个Cache的存活时间都是可以设置和控制的。
d.提供了LRU、LFU和FIFO缓存淘汰算法,Ehcache 1.2引入了最少使用和先进先出缓存淘汰算法,构成了完整的缓存淘汰算法。
e.提供内存和磁盘存储,Ehcache和大多数缓存解决方案一样,提供高性能的内存和磁盘存储。
f.动态、运行时缓存配置,存活时间、空闲时间、内存和磁盘存放缓存的最大数目都是可以在运行时修改的。
g.监听器可以插件化。Ehcache 1.2提供了CacheManagerEventListener和CacheEventListener接口,实现可以插件化,并且可以在ehcache.xml里配置。节点发现,冗余器和监听器都可以插件化。
h.将缓存条目刷到磁盘的操作可以通过cache.flush()方法来执行,这大大方便了Ehcache的使用。
i.缓存管理器监听器。允许注册实现了CacheManagerEventListener接口的监听器:
notifyCacheAdded(),notifyCacheRemoved()缓存事件监听器。它提供了许多对缓存事件发生后的处理机制:notifyElementRemoved/Put/Updated/Expired
j.分布式缓存

24.Java 注解
a.定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
b.作用分类:
①编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
② 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】

c.自定义注解:它类似于新创建一个接口类文件,但为了区分,我们需要将它声明为@interface,如下例:
public @interface NewAnnotation {
}
public class AnnotationTest {
@NewAnnotation
public static void main(String[]args) {
}
}
为自定义注解添加变量:
public @interface NewAnnotation {
String value();
}
public class AnnotationTest {
@NewAnnotation("mainmethod")
public static void main(String[]args) {
saying();
}

@NewAnnotation(value="saymethod")
public static void saying() {
}
}
d.读取注解信息的方法(反射)或通过注解处理器来处理

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
//读取注解信息
public class ReadAnnotationInfoTest {
public static void main(String[] args) throws Exception {
// 测试AnnotationTest类,得到此类的类对象
Class c = Class.forName("com.iwtxokhtd.annotation.AnnotationTest");
Method[] methods = c.getDeclaredMethods();// 获取该类所有声明的方法
Annotation[] annotations;// 声明注解集合
for (Method method : methods) {// 遍历所有的方法得到各方法上面的注解信息
annotations = method.getDeclaredAnnotations();//获取每个方法上面所声明的所有注解信息
System.out.println(method.getName());// 再遍历所有的注解,打印其基本信息
for (Annotation an : annotations) {
System.out.println("方法名为:" + method.getName() + "其上面的注解为:" + an.annotationType().getSimpleName());
Method[] meths = an.annotationType().getDeclaredMethods();
for (Method meth : meths) {// 遍历每个注解的所有变量
System.out.println("注解的变量名为:" + meth.getName());
}
}
}
}
}

25.数据库连接池
1.在Java中开源的数据库连接池有以下几种 :   
a.C3P0:C3P0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。
b.Proxool:这是一个Java SQL Driver驱动程序,提供了对选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中。完全可配置。快速,成熟,健壮。可以透明地为你现存的JDBC驱动程序增加连接池功能。
c.Jakarta DBCP:DBCP是一个依赖Jakarta commons-pool对象池机制的数据库连接池.DBCP可以直接的在应用程序用使用。
d.DDConnectionBroker: DDConnectionBroker是一个简单,轻量级的数据库连接池。   
e.DBPool: DBPool是一个高效的易配置的数据库连接池。它除了支持连接池应有的功能之外,还包括了一个对象池使你能够开发一个满足自已需求的数据库连接池。   
f.XAPool: XAPool是一个XA数据库连接池。它实现了javax.sql.XADataSource并提供了连接池工具。
g.Primrose:Primrose是一个Java开发的数据库连接池。当前支持的容器包括Tomcat4&5,Resin3与JBoss3.它同样也有一个独立的版本可以在应用程序中使用而不必运行在容器中。Primrose通过一个web接口来控制SQL处理的追踪,配置,动态池管理。在重负荷的情况下可进行连接请求队列处理。
h.SmartPool: SmartPool是一个连接池组件,它模仿应用服务器对象池的特性。SmartPool能够解决一些临界问题如连接泄漏(connection leaks),连接阻塞,打开的JDBC对象如Statements,PreparedStatements等. SmartPool的特性包括支持多个pools,自动关闭相关联的JDBC对象, 在所设定time-outs之后察觉连接泄漏,追踪连接使用情况, 强制启用最近最少用到的连接,把SmartPool"包装"成现存的一个pool等。   
i.MiniConnectionPoolManager: MiniConnectionPoolManager是一个轻量级JDBC数据库连接池。它只需要Java1.5(或更高)并且没有依赖第三方包。
j.BoneCP: BoneCP是一个快速,开源的数据库连接池。帮你管理数据连接让你的应用程序能更快速地访问数据库。比C3P0/DBCP连接池快25倍。

2.举例讲解C3P0连接池的使用方法:
a.在当前.java目录下配置一个c3p0.properties文件
#驱动
c3p0.driverClass=com.mysql.jdbc.Driver
#地址
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/jdbc
#用户名
c3p0.user=root
#密码
c3p0.password=lovejava
#-------------------------------
#连接池初始化时创建的连接数
c3p0.initialPoolSize=3
#连接池保持的最小连接数
c3p0.minPoolSize=3
#连接池在无空闲连接可用时一次性创建的新数据库连接数,default:3
c3p0.acquireIncrement=3
#连接池中拥有的最大连接数,如果获得新连接时会使连接总数超过这个值则不会再获取新连接,而是等待其他连接释放,所以这个值有可能会设计地很大,default : 15
c3p0.maxPoolSize=15
#连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接,单位秒
c3p0.maxIdleTime=100
#连接池在获得新连接失败时重试的次数,如果小于等于0则无限重试直至连接获得成功
c3p0.acquireRetryAttempts=30
#连接池在获得新连接时的间隔时间
c3p0.acquireRetryDelay=1000
b.封装ComboPooledDataSource
package com.study.pool;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class ConnectionPool {
private DataSource ds;
private static ConnectionPool pool;
private ConnectionPool(){
ds = new ComboPooledDataSource();
}
public static final ConnectionPool getInstance(){
pool = new ConnectionPool();
}
public static final void DisConnectionPool(){
if(pool!=null){pool.close();pool=null;}
}
public synchronized final Connection getConnection() {
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}

c.使用连接池
package com.study.pool;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class PoolThread extends Thread {
@Override
public void run(){
ConnectionPool pool = ConnectionPool.getInstance();
Connection con = null;
PreparedStatement stmt= null;
ResultSet rs = null;
try{
con = pool.getConnection();
stmt = con.prepareStatement("select sysdate as nowtime from dual");
rs = stmt.executeQuery();
while(rs.next()){
System.out.println(Thread.currentThread().getId()+"---------------开始"+rs.getString("nowtime"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
rs.close();
stmt.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getId()+"--------结束");
}
}

d.main

public class PoolMain {
/**
* 数据源缓冲池 实例练习
*/
public static void main(String[] args) {
System.out.println("缓冲池模拟开始");
PoolThread[] threads = new PoolThread[50];
for(int i=0;i<threads.length;i++){
threads[i] = new PoolThread();
}
for(int i=0;i<threads.length;i++){
threads[i].start();
}
ConnectionPool.DisConnectionPool();
}

}

26.Swing界面响应与线程安全 http://blog.itpub.net/13685345/viewspace-374940/ 1.Swing单线程图形工具包:UI更新只能通过单一的UI事件分发线程来完成。因为UI组件的UI更新API大部分都不是线程安全的。比如:JBUTTON和JLABLED的show()方法,如果他们都不同的线程中被同时调用,将会使得UI更新产生异常。
2.Swing单线程UI更新的工作流程:首先只要存在UI,就会Swing就会自动创建唯一的ui事件队列和唯一的事件分发线程EDT。并发GUI请求(如点击事件,ui更新事件,控件show调用事件...)将被送入隐形的唯一的ui事件队列,该事件队列可以自动接收窗口事件,而用户的任务可以通过SwingUtilities.invokeLater()或invokeAndWait()插入到该队列中去, Timer和SwingWorker也可以向该队列中插入任务;隐形的唯一的事件分发线程(单线程)依次的执行队列中的任务(Runable接口,可以是普通的任务)或事件(UI事件的处理通过执行该控件挂载的处理函数来完成),所以最终的UI更新其实是由隐形的事件分发线程来完成的。

2.Swing的UI线程
Swing组件不支持多线程访问,程序要操作或更改界面的内容,必须向单一线程提出请求,我们把这个单一的线程称为事件派发线程(UI线程)。
Swing是线程不安全的,所有对于UI元素的修改都必须提交给事件分发线程执行。不能在main thread或者其他任何线程中直接操作UI的内容。
如果需要从UI线程或者绘制代码以外的地方访问UI,需要使用SwingUtilities.invokeLater()或invokeAndWait()方法。这2个方法可以将Runnable任务添加到UI事件队列中,由ui事件分发线程执行。
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() { //向UI队列添加一个任务,一边是UI初始化任务
@Override
public void run() {
JForm1 frame = new JForm1();
frame.setTitle("title");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}

3.执行耗时任务
由于UI线程需要不断的执行UI的更新及实践响应,所在UI线程中不可以执行耗时的UI任务。
执行耗时UI任务有2种方式:SwingWorker或者Timer任务他们可以向UI线程发出UI更新请求,并保证线程安全。
a.定时器javax.swing.Timer
private void button1MouseClicked(MouseEvent e) {
//timer所执行的动作,1000millis之后会触发
//javax.swing.Timer 是该包中的Timer哦
Timer timer = new Timer(1000, new ActionListener() { //javax.swing.Timer向UI队列插入定时任务
@Override
public void actionPerformed(ActionEvent e) {
value += 10;
progressBar1.setValue(value); //在Timer任务中执行UI更新
slider1.setValue(value);
if(value == 100){
value = 0;
}
}
});
timer.setRepeats(true);//如果这里不设定,timer中的listener只执行一次
timer.start();
}
b.Swing工作者SwingWorker(http://zhangjunhd.blog.51cto.com/113473/34727/)
该类可以将耗时任务在后台线程中执行,然后将UI更新任务插入队列,并建立后台任务与UI更新任务之间的通信与触发关系。 SwingWorker类可以管理普通任务线程和EDT之间的交互。有助于分离SwingEDT和任务线程,使它们各负其责:对于EDT来说,就是绘制和更新界面,并响应用户输入;对于任务线程来说,就是执行和界面无直接关系的耗时任务和I/O密集型操作。
b.1SwingWorker提供一些通信与控制的特征:
[1]SwingWorker的子类可以定义一个方法,done。当后台任务完成的时候,它将自动的被事件派发线程调用。
[2]SwingWorker类实现java.util.concurrent.Future。这个接口允许后台任务提供一个返回值给其他线程。该接口中的方法还提供允许撤销后台任务以及确定后台任务是被完成了还是被撤销的功能。
[3]后台任务可以通过调用SwingWorker.publish来提供中间结果,事件派发线程将会调用该方法。
[4]后台任务可以定义绑定属性。绑定属性的变化将触发事件,事件派发线程将调用事件处理程序来处理这些被触发的事件。

b.2实际上有两个方法来得到doInBackground类返回的对象。
[1]调用SwingWorker.get没有参数。如果后台任务没有完成,get方法将阻塞直到它完成。
[2]调用SwingWorker.get带参数指定timeout。如果后台任务没有完成,阻塞直到它完成-除非timeout期满,在这种情况下,get将抛出java.util.concurrent.TimeoutException。

SwingWorker worker = new SwingWorker<ImageIcon[], classname>() { //doInBackground返回结果类型,pulish传输类型
@Override
public ImageIcon[] doInBackground() { //后台任务,他不会插入到UI队列中,所以在他里面不能调用UI接口
final ImageIcon[] innerImgs = new ImageIcon[nimgs];
for (int i = 0; i < nimgs; i++) {
innerImgs[i] = loadImage(i+1);
publish(new classname());
}
return innerImgs; //返回结果,会使worker.get()阻塞
}

@Override
public void done() { //done会被插入到UI队列中执行,调用是在doInBackground完成之后
//Remove the "Loading images" label.
animator.removeAll();
loopslot = -1;
try {
imgs = get();
} catch (InterruptedException ignore) {}
catch (java.util.concurrent.ExecutionException e) {
String why = null;
Throwable cause = e.getCause();
if (cause != null) {
why = cause.getMessage();
} else {
why = e.getMessage();
}
System.err.println("Error retrieving file: " + why);
}
}
@Override
protected void process(List<classname> list){process会被插入到UI队列中执行,动作发生在每个publish之后,并且接收publish所以数据列表
for(ImageInfo info: infoList) {
if (isCancelled()) { //见下节
break;
}
//处理中间结果
model.addElement(info);
}
}
};
worker.execute();
doInBackground和get和done:在获得执行结果后应使用SwingWorker的get方法获取doInBackground方法的结果。可以在EDT上调用get方法,但该方法将一直处于阻塞状态,直到任务线程完成。最好只有在知道结果时才调用get方法,这样用户便不用等待。为防止阻塞,可以使用isDone方法来检验doInBackground是否完成。另外调用方法get(longtimeout, TimeUnitunit)将会一直阻塞直到任务线程结束或超时。get获取任务结果的最好地方是在done方法内。
在doInBackground方法完成之后,SwingWorker调用done方法。如果任务需要在完成后使用线程结果更新GUI组件或者做些清理工作,可覆盖done方法来完成它们。这儿是调用get方法的最好地方,因为此时已知道线程任务完成了,SwingWorker在EDT上激活done方法,因此可以在此方法内安全地和任何GUI组件交互。
publish和process
SwingWorker在doInBackground方法结束后才产生最后结果,但任务线程也可以产生和公布中间数据。有时没必要等到线程完成就可以获得中间结果。
中间结果是任务线程在产生最后结果之前就能产生的数据。当任务线程执行时,它可以发布类型为V的中间结果,通过覆盖process方法来处理中间结果。
任务对象的父类会在EDT线程上激活process方法,因此在process方法中程序可以安全的更新UI组件。
当从任务线程调用publish方法时,SwingWorker类调度process方法。有意思的是process方法是在EDT上面执行,这意味着可以同Swing组件和其模型直接交互。
如果SwingWorker通过publish发布了一些数据,那么也应该实现process方法来处理这些中间结果,任务对象的父类会在EDT线程上激活process方法,因此在此方法中程序可以安全的更新UI组件。每调用一个publish,process就会被当做任务插入到UI更新线程。
b.3取消后台任务
调用SwingWorker.cancel来取消一个正在执行的后台任务。任务必须与它自己的撤销机制一致。有两个方法来做到这一点:
[1]当收到一个interrupt时,将被终止。
[2]调用SwingWorker.isCanceled,如果SwingWorker调用cancel,该方法将返回true。
b.4绑定属性和状态方法
SwingWorker支持bound properties,这个在与其他线程通信时很有作用。提供两个绑定属性:progress和state。progress和state可以用于触发在事件派发线程中的事件处理任务。
通过实现一个property change listener,程序可以捕捉到progress,state或其他绑定属性的变化。
The progress Bound Variable
Progress绑定变量是一个整型变量,变化范围由0到100。它预定义了setter (the protected SwingWorker.setProgress)和getter (the public SwingWorker.getProgress)方法。
The state Bound Variable
State绑定变量的变化反映了SwingWorker对象在它的生命周期中的变化过程。该变量中包含一个SwingWorker.StateValue的枚举类型。可能的值有:
[1]PENDING
这个状态持续的时间为从对象的建立知道doInBackground方法被调用。
[2]STARTED
这个状态持续的时间为doInBackground方法被调用前一刻直到done方法被调用前一刻。
[3]DONE
对象存在的剩余时间将保持这个状态。
需要返回当前state的值可调用SwingWorker.getState。
Status Methods
两个由Future接口提供的方法,同样可以报告后台任务的状态。如果任务被取消,isCancelled返回true。此外,如果任务完成,即要么正常的完成,要么被取消,isDone返回true。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息