欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CoreJava学习心得17

程序员文章站 2022-07-13 21:37:34
...
JAVA5.0 的注释 (Annotation)

描述代码的代码。给编译器看的代码,作用是规范编译器的语法。
class Student{
	@Override
	public String toString(){
		return “student”;
	}

}

类型(接口)
1. 标记注释
标记注释中没有属性,所以也不需要为属性赋值
@Override

2. 单值注释
单值注释中只能定义一个属性。
@注释名(prameter=10)
int parameter

特例:
@注释名 (value “134” )(当单值注释的属性名为value时,可以省略属性名,直接写值)

@SuperessWarning({“ddd”,”aaa”,”ccc”})  //JVM还没有实现这个注释

3.普通注释(多值注释)
(key1=value,……)

4.自定义注释
public  @interface Test{

}

在自定义注释时,要用注释来注释(描述)注释。
@Target(value={……}),用来描述自定义注释所适用的程序元素的种类。单值注释
这个注释的值只能是ElementType枚举值,只能使用以下的值
ANNOTATION_TYPE 注释类型声明
CONSTRUCTOR 构造方法声明
FIELD 属性声明(包括枚举常量)
LOCAL_VARIABLE 局部变量声明
METHOD 方法声明 (常用)
PACKAGE 包声明
PARAMETER 参数声明
TYPE 类、接口(包括注释类型)或枚举声明(常用)
例:
@Target({ElementType.METHOD, ElementType .TYPE})//表示这个注释可用的范围,这个注释可以用在方法和类型及接口。

@Retention(value=……),描述(注释)注释能够保留到什么时候。单值注释
其中的值只能是以下的RetentionPolicy枚举的值
CLASS 编译器将把注释记录在类文件中,但在运行时 VM 不需要保留注释。
RUNTIME  编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。
SOURCE 编译器要丢弃的注释。
例:
@Retention(RetentionPolicy.RUNTIME)
// 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。

@Documented表示某一类型的注释将通过 javadoc 和类似的默认工具进行文档化

@Inherited 表示注释类型该注释会被自动继承

注释的属性类型可以是8种基本类型、String、Enum、Class、Annotation以及它们的数组

例:
@Test()
public class MyClass2 {
	@Test()
	public void study(String name){
		System.out.println("Study "+name);
	}
	
	@Test(value="Meal")//单值注释
	@Author(value={@Name(firstName="Liu",lastName="Chunyang"),@Name(firstName="Xue",lastName="Hiloo")})//单值注释中是用数组类型地赋值方法
	public void eat(String food){
		System.out.println("Eat "+food);
	}
}


自定义注释的写法
例:
import java.lang.annotation.*;
@Target({ElementType.METHOD})
//表示这个注释可用的范围,这个注释只能用在方法之上。
@Retention(RetentionPolicy.RUNTIME)
// 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。public @interface Author {
	Name[] value();
/*
定义注释的属性,注意属性后面要价括号,这个属性既是属性又是方法,可以返回属性的值
*/
}

import java.lang.annotation.*;
@Target({ElementType.METHOD,ElementType.TYPE})//这个注释可用于类,接口和方法
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
	String value() default "ABC" ;//定义注释的属性及其默认值
}

处理自定义注释的类
例:
import java.lang.reflect.*;
public class TestFramework {
	public static void main(String[] args) throws Exception {
		Class c=Class.forName("MyClass2");//获取有注释的类的类对象
		Method[] ms=c.getMethods();//获得该类中有的方法对象
		Object o=c.newInstance();//获得该类的实例
		for(Method m:ms){
			boolean flag=m.isAnnotationPresent(Test.class);
/*
isAnnotationPresent(Class c)这个方法是判断这个方法是不是加上了注释注意这是方法对象的方法
*/
			if (flag){
				Test t=m.getAnnotation(Test.class);
/*
getAnnotation(Class c)方法是获得该方法的注释,得到注释对象t
可以通过注释对象 ”属性名()”也就是value(),获得注释的属性的值
*/
				String value=t.value();
				m.invoke(o,value);
			}
			
			flag=m.isAnnotationPresent(Author.class);
			if (flag){
				Author a=m.getAnnotation(Author.class);
				Name[] ns=a.value();
				for(Name n:ns){
					String fn=n.firstName();
					String ln=n.lastName();
					System.out.println(fn+ "  "+ ln);
				}
			}
		}
		
	}

}


例2:
定义注释
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
	String name();
	String sex();
}

使用注释
public class TestAnntion {
	@Test(name="liucy",sex="man")
	public void test(String name,String sex){
		System.out.println(name+" "+sex);
	}
}

处理注释

import java.lang.reflect.Method;
public class TestT {

	public static void main(String[] args) throws Exception {
		Class c=Class.forName(args[0]);
		Method[] ms=c.getMethods();
Object o=c.newInstance();
for(Method m:ms){
			boolean flag=m.isAnnotationPresent(Test.class);
			if (flag){
				Test t=m.getAnnotation(Test.class);
				String name=t.name();
				String sex=t.sex();
				m.invoke(o, name,sex);
			}		
	}

}


三个新加的多线程包

Java 5.0里新加入了三个多线程包:
java.util.concurrent, java.util.concurrent.atomic, java.util.concurrent.locks.

java.util.concurrent包含了常用的多线程工具,是新的多线程工具的主体。
java.util.concurrent.atomic包含了不用加锁情况下就能改变值的原子变量,比如说AtomicInteger提供了addAndGet()方法。Add和Get是两个不同的操作,为了保证别的线程不干扰,以往的做法是先锁定共享的变量,然后在锁定的范围内进行两步操作。但用AtomicInteger.addAndGet()就不用担心锁定的事了,其内部实现保证了这两步操作是在原子量级发生的,不会被别的线程干扰。
java.util.concurrent.locks包包含锁定的工具。

Callable 和 Future接口

Executor接口替代了Thread类,他可以创建定量的和动态以及周期性的线程池。
ExecutorService接口,线程池,用来存放线程来节省创建和销毁资源的消耗。

Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
Callable和Runnable有几点不同:
Callable规定的方法是call(),而Runnable规定的方法是run().
Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
call()方法可抛出异常,而run()方法是不能抛出异常的。

Future对象可以获得线程运行的返回值

运行Callable任务可拿到一个Future对象,通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
以下是Callable的一个例子:
public class DoCallStuff implements Callable{ // *1
        private int aInt;
        public DoCallStuff(int aInt) {
                this.aInt = aInt;
        }
        public String call() throws Exception { //*2
                boolean resultOk = false;
                if(aInt == 0){
                        resultOk = true;
                }  else if(aInt == 1){
                        while(true){ //infinite loop
                                System.out.println("looping....");
                                Thread.sleep(3000);
                        }
                } else {
                        throw new Exception("Callable terminated with Exception!"); //*3
                }
                if(resultOk){
                        return "Task done.";
                } else {
                        return "Task failed";
                }
        }
}

*1: 名为DoCallStuff类实现了Callable,String将是call方法的返回值类型。例子中用了String,但可以是任何Java类。
*2: call方法的返回值类型为String,这是和类的定义相对应的。并且可以抛出异常。
*3: call方法可以抛出异常,如加重的斜体字所示。
以下是调用DoCallStuff的主程序。
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Executor {
        public static void main(String[] args){
                //*1
                DoCallStuff call1 = new DoCallStuff(0);
                DoCallStuff call2 = new DoCallStuff(1);
                DoCallStuff call3 = new DoCallStuff(2);
                //*2
                ExecutorService es = Executors.newFixedThreadPool(3);
                //*3
                Future future1 = es.submit(call1);
                Future future2 = es.submit(call2);
                Future future3 = es.submit(call3);
                try {
                        //*4
                        System.out.println(future1.get());
                         //*5
                        Thread.sleep(3000);
                        System.out.println("Thread 2 terminated? :" + future2.cancel(true));
                        //*6
                        System.out.println(future3.get());
                } catch (ExecutionException ex) {
                        ex.printStackTrace();
                } catch (InterruptedException ex) {
                        ex.printStackTrace();
                }
        }
}

*1: 定义了几个任务
*2: 初始了任务执行工具。任务的执行框架将会在后面解释。
*3: 执行任务,任务启动时返回了一个Future对象,如果想得到任务执行的结果或者是异常可对这个Future对象进行操作。Future所含的值必须跟Callable所含的值对映,比如说例子中Future对印Callable
*4: 任务1正常执行完毕,future1.get()会返回线程的值
*5: 任务2在进行一个死循环,调用future2.cancel(true)来中止此线程。传入的参数标明是否可打断线程,true表明可以打断。
*6: 任务3抛出异常,调用future3.get()时会引起异常的抛出。
运行Executor会有以下运行结果:
looping....
Task done. //*1
looping....
looping....//*2
looping....
looping....
looping....
looping....
Thread 2 terminated? :true //*3
//*4
java.util.concurrent.ExecutionException: java.lang.Exception: Callable terminated with Exception!
        at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:205)
        at java.util.concurrent.FutureTask.get(FutureTask.java:80)
        at concurrent.Executor.main(Executor.java:43)
        …….
*1: 任务1正常结束
*2: 任务2是个死循环,这是它的打印结果
*3: 指示任务2被取消
*4: 在执行future3.get()时得到任务3抛出的异常

lock接口

实现类ReentrantLock

我们可以用lock对象,来对临界资源加锁调用lock对象的lock()方法,使得没有得到锁的线程阻塞,解锁则调用lock对象的unlock()方法,并且释放锁,只有获得lock对象才能访问临界资源,如果没有获得lock对象,就会进入lock对象的锁池。trylock()方法会返回布尔值,这个方法是用来判断这个锁对象是不是已经被线程获取,如果返回值为true,则会直接获得这个锁对象,如果返回false,线程不会阻塞还会继续运行。
Lock lock=new ReentrantLock();
publci void test(){
   try{
if(lock.trylock){//判断锁是否已经分配出去
lock.lock();
//如果锁没有被分配,就会获得锁,没有得到锁,就阻塞
}else{
......
}
.....//需要加锁的临界资源。
}finally{
lock.unlock();//解锁,释放锁。
}
}

ReadWriteLock读写锁接口

ReentrantReadWriteLock是ReadWriteLock的实现类。

ReentrantReadWriteLock的Lock  readLock()方法会分配读锁对象,读锁可以分配多个线程,但是在分配读锁后所有读锁释放前,写锁是不能被分配的。
ReentrantReadWriteLock的Lock  writeLock()方法会分配写锁对象,且只能分配给一个线程,在分配写锁后,在写锁释放前,读锁是不能被分配。

Condition接口和实现类

Condition是等待对列的对象,它是通过lock对象的newCondition()方法得到的
Condition实现类的await()替代了wait()方法。
notify(),notifyAll() 在JDK5.0中已经用Condition实现类的signal() ,signalAll()方法替换掉了,在JDK5.0中,可以使用多个等待队来存放等待的线程,并对线程进行分类。

Queue接口(Collection的子接口,队列接口)

LinkedList也实现了这个在JDK5.0中的新接口Queue,并且这个类自动的实现了生产者和消费者的同步。


JDK5.0的高级同步

Semaphore类(信号量)也就是可以向线程分配许可证,指定许可证数量可以实现多线程的同步。
Semaphore s=new Semaphore(4);
//可以分配4个许可证,许可证都被分配出去时,得不到许可证的线程就会阻塞。

Semaphore类的acquire(…)方法,获得许可证。Semaphore类的release(…) 方法,释放一个许可证,也有相应的方法指定释放和获得许可证的数量的方法。

CountDownLatch类

CountDownLatch中有个计数器,访问这个类的对象就会从计数器中减一,countDown()方法会将原有的设置的计数器值减一,当countdown计数器为零时会使放所有await()的线程。

CyclicBarrier类

CyclicBarrier和CountDownLatch比较相似
CyclicBarrier在构造时给出线程数,只有等待的线程数到了构造方法中指定的数量,当最后一个线程等待后,所有的线程都会被释放,这个类是一个多线程汇合的工具。

Exchanger类,用exchange()方法可以使两个线程间相互交换对象,在两线程的同步点,等待第二个线程。在同步点时,交换对象,并同时被释放。