java高新技术篇
------- android培训、java培训、期待与您交流! ----------
1.静态导入与编译器语法设置
import语句可以导入一个类或某个包中的所有类;import static语句导入一个类中的某个静态方法或所有静态方法。
语法举例:
使用求2个数的最大值和相减结果的绝对值进行举例
System.out.println(min(3,5));
System.out.println(abs(2-9));
import static java.lang.Math.max; ---每次写一个方法较麻烦
import static java.lang.Math.*;
public static void main(String[] args) {
2.可变参数与OverLoad
1.一个方法接受的参数个数不固定 有时需要两个数相加 有的需要三个数相加
2.overload(重载) vs override(重写)
package cn.itcost.day1;
public class VaribleParameter {
}
可变参数只能出现在参数列表的最后;
调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数;
...位于变量类型和变量名之间,前后有无空格都可以
3.增强for循环
1.首先增强for循环和iterator遍历的效果是一样的,也就说增强for循环的内部也就是调用iteratoer实现的,
但是增强for循环有些缺点,例如不能在增强循环里动态的删除集合内容。不能获取下标等。
2.ArrayList由于使用数组实现,因此下标明确,最好使用普通循环。
3.而对于LinkedList 由于获取一个元素,要从头开始向后找,因此建议使用增强for循环,也就是iterator。
示例代码:
/**
* 增强for循环用法
*
*/
public class ForTest {
public static void main(String[] args) {
//List<Integer> list = new ArrayList<Integer>();
List<Integer> list=new LinkedList<Integer>();
for (int i=0; i<50000; i++) {
list.add(11);
}
int resutl = 0;
long start = System.currentTimeMillis();
for (int i=0; i< list.size(); i++) {
resutl = list.get(i);
}
System.out.println("普通循环使用了"+ (System.currentTimeMillis()- start)+"毫秒");
start = System.currentTimeMillis();
for (int c2 : list) {
}
System.out.println("增强for循环使用了"+ (System.currentTimeMillis()- start)+"毫秒");
}
}
4.基本数据的自动拆装箱及享元设计模式
Integer i1 = 1;
这行代码在JDK1.5之前是错误的。因为左边是基本数据类型,而右边是整数。
JDK1.5之后,Integer i1 = 1是正确的,右边的1将自动被装箱为基本数据类型。
public static void main(String[] args){
Integer i1 = 1;
Integer i2 = 1;
Integer i3 = 211;
Integer i4 = 211;
System.out.println(i1 == i2); //此处返回true
System.out.println(i3 == i4); //返回false
}
}
i1与i2相同,i3与i4不相同。
导致这个区别的原因是:在-128到127之间的基本数据类型将被缓存,因为它们经常会被使用,系统将在内存中查找之前被定义的
对象。 超过这个范围的将不被缓存。
享元设计模式:当某个对象经常被调用,并且有大部分相同属性很少修改,可以将它们相同部分(内部状态)剥离出来,设计成共享对象,其中不同的
属性设计成方法的参数(外部状态)。
5.枚举的作用介绍
问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别表示星期一到星期日,但有人可能会写成int weekday = 0;或即使使用常量方式也无法阻止意外。
枚举-----就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。
6.用普通类模拟枚举的实现原理
示例代码:
自定义一个WeekDay类:
public
测试类:
public class EnumTest {
}
用枚举实现类WeekDay如下:
public
}
7.枚举的基本应用
1 枚举类型等效于定义static finally常量
2. 枚举类型可以有自己的属性(参数)和方法
3. 枚举类型可以以独立的文件存在
最简单的枚举类型:
public enum Belle {
para1, para2, para3, para4;
}
调用:
for (Belle belle : Belle.values()) { //遍历枚举中所有的对象
System.out.println(belle.name()); //输出每一个枚举的名称
}
要取出某个指定名称的枚举成员 可用 Belle.para1(注意:因为枚举对象默认是static final其成员对象都是stastic属性,直接调用),也可以用Belle.valueOf("para1")
稍复杂的枚举类型:
public enum Belle {
para1("this is para1"),
para2("this is para3"),
para3("this is para3"),
para4("this is para4");
public final String str; //定义枚举成员属性
Belle(String str) { //内部方法设定枚举成员属性
this.str = str;
}
}
这样就每一个枚举成员就有了属性,可以定义多个属性,每个属性必须在内部方法中set(例如this.str = str;)
调用时用这样的格式取值 belle.str
枚举成员就的属性可以不光是字符串,可以是任意对象
8.实现带有构造方法的枚举
示例代码:
publc enum WeekDay{
}
构造方法必须是私有的,且放在所有枚举元素的后面。构造方法可带参数。
9.实现带有抽象方法的枚举
示例代码:
public enum TrafficLamp{
//枚举交通灯TrafficLamp 的元素实例有3个:RED,GREEN,YELLOW.
//当枚举中只有一个元素的时候,它是单例模式。
10.透彻分析反射的基础_Class类
简述
Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则有这个类的实例对象来确定的,不同的实例对象有不同的属性值。Java程序中的各个java类,他们是否属于同一类事物,是不是可以用一个类来描述这些类事物呢?这二个类的名字就是Class,要注意于小写的class关键字的区别,Class类描述了哪些方面的信息呢?类的名字,类的访问属性,类所属的包,字段对象的列表,方法名称的列表,等等。学习反射,首先要明白Class这个类。
反射的基石—>Class类
<!--[if !supportLists]-->一、<!--[endif]-->Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class
<!--[if !supportLists]-->二、<!--[endif]-->对比提问,众多的人用一个什么类表示?众多的Java类用一个什么类表示?
人---àPerson
Java类----àClass
<!--[if !supportLists]-->三、<!--[endif]-->对比提问,Person类代表人,他的实例对象就是张三、李四这样一个个具体的人,
Class类代表的是Java类,他的各个实例对象又分为什么呢??
*对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。
*一个类被类加载器加载到内存中,占用的一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以他们在内存中内容也是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?就是Class
如何得到各个字节码对应的实例对象(Class类型)
1、类名.class 例如 System.class
2、对象.getClass() 例如 new Date().getClass()
3、Class.forName(“类名”) 例如 Class.forName(“java,util.Date”)
九个预定义Class实例对象
1 public boolean isPrimitive()
判定指定的 Class 对象是否表示一个基本类型,即Class.isPrimitive()
有九种预定义的 Class
对象,表示八个基本类型和 void。这些类对象由 Java虚拟机创建,与其表示的基本类型同名,即boolean
、byte
、char
、short
、int
、long
、float
和double
。
这些对象仅能通过下列声明为 public static final 的变量访问,也是使此方法返回 true
的仅有的几个 Class
对象。
2
Boolean.TYPE==boolean.class,Character.TYPE==char.class,Byte.TYPE==byte.class,Short.TYPE==short.class,Integer.TYPE=int.class,Long.TYPE==long.class,Float.TYPE==float.class,Double.TYPE==double.class,Void.TYPE==void.class
数组类型的Class实例对象
Class.isArray()
总之,只要是在源程序中出现的类型,都有各自的Class的实例对象,例如 int[] , void
一个奇怪的问题:加载了字节码,并调用了其getMethods之类的方法,但是没有看到类的静态代码块被执行,只有在第一个实例对象被创建时,这个静态代码才会被执行。准确的说,静态代码块不是在类加载时被调用的,而是第一个实例对象被创建时才执行的。
示例代码:
- package com.zyj.day1;
- public class ReflectTest {
- public static void main(String[] args) throws ClassNotFoundException {
- String str1="abc";
- //返回String的字节码
- Class cls1=str1.getClass();
- Class cls2=String.class;
- Class cls3=Class.forName("java.lang.String");
- System.out.println(cls1==cls2);//true
- System.out.println(cls1==cls3);//true
- System.out.println(cls1.isPrimitive());//false isPrimitive()是判断是否是基本类型的字节码
- System.out.println(void.class.isPrimitive());//true
- System.out.println(int.class.isPrimitive());//true
- System.out.println(int.class==Integer.class);//false
- System.out.println(int.class==Integer.TYPE);//true TYPE是包装类型中的基本类型的字节码
- System.out.println(int[].class.isPrimitive());//false
- System.out.println(int[].class.isArray());//true
- }
- }
11.反射的概念
在java中,反射是一种强大的工具,它使您能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行
源代表链接。反射允许我们在编写与执行时,使我们的程序代码能够接入装载到JVM中的类的内部信息,而不是源代码
中选定的类协作的代码,这使反射成为构造灵活的应用的主要工具。使用反射要注意的是使用不当时,反射成本很高。
12.构造方法的反射应用
示例代码:
public class ReflectTest {
public static void main(String[] args) throws Exception{
//构造参数没有数字顺序,只能够通过参数类型来进行判别;
//String.class.getConstructor(StringBuffer.class,int.class); //字符串类型字节码 获取构造方法对象
Constructor<String> constructor1 = String.class.getConstructor(StringBuffer.class); //取得一个String Buffer参数类型的构造方法
String str2 = (String)constructor1.newInstance(new StringBuffer("abc")); //实例化
System.out.println(str2.charAt(2));
}
}
class -- constructor -- object //由class得到constructor,再由constructor得到new object
13.成员变量的反射
示例代码:
public class Test_5 {
public static final int a = 111;
public static final int b = 222;
public static final int c = 333;
public static final int d = 444;
public static void main(String[] args) {
try {
Class c = Class.forName("com.test.Test_5");
Field[] fields = c.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
String m = Modifier.toString(fields[i].getModifiers());
if (m != null && m.indexOf("final") > -1) {
System.out.println(fields[i].getInt(int.class));
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
14.成员方法的反射
示例代码:
public class TestRef {
public staticvoid main(String args[])throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
Foo foo = new Foo("这个一个Foo对象!");
Class clazz = foo.getClass();
Method m1 = clazz.getDeclaredMethod("outInfo");
Method m2 = clazz.getDeclaredMethod("setMsg", String.class);
Method m3 = clazz.getDeclaredMethod("getMsg");
m1.invoke(foo);
m2.invoke(foo, "重新设置msg信息!");
String msg = (String) m3.invoke(foo);
System.out.println(msg);
}
}
class Foo {
private String msg;
public Foo(String msg) {
this.msg = msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void outInfo() {
System.out.println("这是测试Java反射的测试类");
}
}
15.对接收数组参数的成员方法进行反射
示例代码:
public static void main(String[] args)throws Exception {
}
}
class TestArguments{
}
16.数组与Object的关系及其反射类型
int [] a1=new int[3];
具有相同的维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象
代表数组的Class实例对象的个头Superclass方法返回的父类为Object类对应的class
基本类型的一维数组可以被当做Object类型使用个,不能当做Object[]类型使用;非基本类型的一维数组,既可以当做Object类使用,又可以当做Object[]类型使用
Arrays.asList方法处理int[]和String[]时的差异
Array工具类用于完成对数组的反射操作,不能得到数组的类型,可以得到数组中元素的类型
a[0].getClass().getName();
}
17.数组的反射应用
示例代码:
public classReflectLoadMain{
public static void main(String[] args)throwsException{
String[] a4=newString[]{"a","b","c"};
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
printObject(a4);
printObject("xyx");
}
private static void printObject(Object obj){
// TODO Auto-generated method stub
Class cls = obj.getClass();
if (cls.isArray()){
int len = Array.getLength(obj);
for(int i=0;i<len;i++){
System.out.println(Array.get(obj, i));
}
}
else {
System.out.println(obj);
}
}
}
18.ArrayList_HashSet的比较及Hashcode分析
public class ReflectPoint {
}
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class ReflectTest2 {
}
hashcode把集合分为若干值域,把将要存入的数据转化为HashCode值后放入不同的区域,
当对象被存储进HashCode集合中以后,就不能修改这个对象中的那些参与计算哈希值得字段了,否则,对象修改后的哈希值与最近存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains 方法使用该对象当前的引用作为参数去HashSet集合检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。
19.框架的概念
框架概念:是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大块内容:对外的接口、接口的实现和对集合运算的算法。 接口:即表示集合的抽象数据类型。接口提供了让我们对集合中所表示的内容进行单独操作的可能。 实现:也就是集合框架中接口的具体实现。实际它们就是那些可复用的数据结构。 算法:在一个实现了某个集合框架中的接口的对象身上完成某种有用的计算的方法,例如查找、排序等。这些算法通常是多态的, 因为相同的方法可以在同一个接口被多个类实现时有不同的表现。事实上,算法是可复用的函数。如果你学过C++, 那C++中的标准模版库(STL)你应该不陌生,它是众所周知的集合框架的绝好例子。
20.用类加载器的方式管理资源和配置文件
用java读取properties文件
获得配置文件的 第一种 方法:
InputStream ips =newFileInputStream("config.properties");
Properties props =newProperties();
props.load(ips);
ips.close();
一定要用完整的路径,但完整的路径不是硬编码,而是运输出来的
获得资源文件的 第二种 方式:(最常用的,但不能取代 IO 的方式)
每一个 .class 文件都被加载到内存中,这是类加载器的功能。类加载器不但能加载.class文件,还能加载普通文件,例:
InputStream ips = ReflectHashCode.class.getClassLoader().getResourceAsStream(“cn/itcast/day1/config.properties”);
//在cn/itcase/day1 目录下查找 config.properties 文件
//通过类的class找到类加载器( getClassLoader() ), 然后用类加载器加载普通文件( getResourceAsStream(), 在classpath指定的目录下逐一的查找要加载的文件 )
配置文件都放在 存放类( .class)文件的目录下
获得资源文件的 第三种 方式
class 本身也自带一种加载资源文件的方式,实际上是内部调用了classloader,例:
InputStream ips = ReflectHashCode.getResourceAsStream(“config.properties”);
只需要写要加载的配置文件的名字就可以,不需要写目录,相对路径,默认在自己所在的包下面查找。如果在所在包的子包下面,路径添加子包名称,例:resource/config.properties
还可以用绝对路径如果上例改为,cn/itcast/day1/config.properties 就成了绝对路径,默认从classpath的根目录下查找,这时候就要从根开始写上完整的绝对路径
21.由内省引出JavaBean的讲解
JavaBean的基本概念:JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。
JavaBean可分为两种:一种是有用户界面(UI,User Interface)的JavaBean;还有一种是没有用户界面,主要负责处理事务(如数据运算,操纵数据库)的JavaBean。JSP通常访问的是后一种JavaBean。 一个JavaBean由3部分组成:
(1) 属性(properties)
JavaBean提供了高层次的属性概念,属性在JavaBean中不只是传统的面向对象的概念里的属性,它同时还得到了属性读取和属性写入的API的支持。属性值可以通过调用适当的bean方法进行。比如,可能bean有一个名字属性,这个属性的值可能需要调用String getName()方法读取,而写入属性值可能要需要调用void setName(String str)的方法。
每个JavaBean属性通常都应该遵循简单的方法命名规则,这样应用程序构造器工具和最终用户才能找到JavaBean提供的属性,然后查询或修改属性值,对bean进行操作。JavaBean还可以对属性值的改变作出及时的反应。比如一个显示当前时间的JavaBean,如果改变时钟的时区属性,则时钟会立即重画,显示当前指定时区的时间。
(2) 方法(method)
JavaBean中的方法就是通常的Java方法,它可以从其他组件或在脚本环境中调用。默认情况下,所有bean的公有方法都可以被外部调用,但bean一般只会引出其公有方法的一个子集。
由于JavaBean本身是Java对象,调用这个对象的方法是与其交互作用的唯一途径。JavaBean严格遵守面向对象的类设计逻辑,不让外部世界访问其任何字段(没有public字段)。这样,方法调用是接触Bean的唯一途径。
但是和普通类不同的是,对有些Bean来说,采用调用实例方法的低级机制并不是操作和使用Bean的主要途径。公开Bean方法在Bean操作中降为辅助地位,因为两个高级Bean特性--属性和事件是与Bean交互作用的更好方式。
因此Bean可以提供要让客户使用的public方法,但应当认识到,Bean设计人员希望看到绝大部分Bean的功能反映在属性和事件中,而不是在人工调用和各个方法中。
(3) 事件(event)
Bean与其他软件组件交流信息的主要方式是发送和接受事件。我们可以将bean的事件支持功能看作是集成电路中的输入输出引脚:工程师将引脚连接在一起组成系统,让组件进行通讯。有些引脚用于输入,有些引脚用于输出,相当于事件模型中的发送事件和接收事件。
事件为JavaBean组件提供了一种发送通知给其他组件的方法。在AWT事件模型中,一个事件源可以注册事件监听器对象。当事件源检测到发生了某种事件时,它将调用事件监听器对象中的一个适当的事件处理方法来处理这个事件。
由此可见,JavaBean确实也是普通的Java对象,只不过它遵循了一些特别的约定而已。
JavaBean三大构件
Java bean:编程语言java中的术语,行业内通常称为java豆,带点美哩口味,飘零着咖啡的味道,在计算机编程中代表java构件(EJB的构件),通常有Session bean,Entity bean,MessageDrivenBean三大类。 Session bean:会话构件,是短暂的对象,运行在服务器上,并执行一些应用逻辑处理,它由客户端应用程序建立,其数据需要自己来管理。分为无状态和有状态两种。 Entity bean:实体构件,是持久对象,可以被其他对象调用。在建立时指定一个唯一标示的标识,并允许客户程序,根据实体bean标识来定位beans实例。多个实体可以并发访问实体bean,事务间的协调由容器来完成。 MessageDriven Bean:消息构件,是专门用来处理JMS(Java Message System)消息的规范(EIB2.0)。JMS是一种与厂商无关的API,用来访问消息收发系统,并提供了与厂商无关的访问方法,以此来访问消息收发服务。JMS客户机可以用来发送消息而不必等待回应。
23.对JavaBean的简单内省操作
使用PropertyDescriptor类,它代表的javabean的属性,封装的是属性的信息
示例代码:
- public class TestIntroSpector {
- public static void main(String[] args) throws Exception {
- // TODO Auto-generated method stub
- ReflectPoint rp = new ReflectPoint(3, 5);
- //使用内省方法
- String propertyName = "x"; // 属性名为x
- Object retValue = getPropery(rp, propertyName);
- System.out.println(retValue);
- Object value = 8;
- PropertyDescriptor dp = new PropertyDescriptor(propertyName, rp.getClass());
- Method methodSetX = dp.getWriteMethod();
- methodSetX.invoke(rp, value);
- System.out.println(rp.getX());
- }
- }
24.对JavaBean的复杂内省操作
示例代码:
- public class TestIntroSpector {
- public static void main(String[] args) throws Exception {
- // TODO Auto-generated method stub
- ReflectPoint rp = new ReflectPoint(3, 5);
- //使用内省方法
- String propertyName = "x"; // 属性名为x
- Object retValue = getPropery(rp, propertyName);
- System.out.println(retValue);
- }
- /**
- * @param rp
- * @param propertyName
- * @return
- * @throws IntrospectionException
- * @throws IllegalAccessException
- * @throws InvocationTargetException
- */
- private static Object getPropery(Object obj, String propertyName)
- throws IntrospectionException, IllegalAccessException,
- InvocationTargetException {
- /*PropertyDescriptor pd = new PropertyDescriptor(propertyName, rp.getClass());
- Method methodGetX = pd.getReadMethod();
- Object retValue = methodGetX.invoke(rp);*/
- BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
- PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
- Object retValue = null;
- for (PropertyDescriptor pd : pds) {
- if (pd.getName().equals(propertyName)) {
- Method methodGetX = pd.getReadMethod();
- retValue = methodGetX.invoke(obj);
- }
- }
- return retValue;
- }
- }
25.使用BeanUtils工具包操作JavaBean
首先导入BeanUtils工具包:
一、先新建一个文件夹,存放BeanUtils工具包:右击工作台java项目的图标-->New-->Folder,新建一个名为lib的目录内,打开文件夹,将工具包放入其中
二、在工作台java项目的视图下,右击工具包-->bulid Path-->Add to Bulid Path,则工具包就被导入项目中了,此工具包中的类和工具就可以使用了。
使用BeanUtils工具包:
BeanUtils.setProperty(p1, "x", "9");//获取p1中属性x的值
BeanUtils.getProperty(p1, "x");//设置p1中属性x的值
22.泛型的基本应用
1、在集合中不使用泛型操作
ArrayList collection1 = new ArrayList();
collection1.add(1); //可以添加 int 类型,编译器有警告
collection1.add(1L); //可以添加 Long 类型,编译器有警告
collection1.add("abc"); //可以添加 String类型,编译器有警告
Object obj = (Object)collection1.get(1); //不能确定得到的结果是什么类型,使用时需要强制类型转换。
2、在集合中使用泛型操作:
ArrayList<String> collection2 =new ArrayList<String>();
//collection2.add(1); //错误的代码,只能添加String类型数据。
collection2.add("abc"); //正确添加
String str = collection2.get(0); //读取数据时不需要强制类型转换。
3、在反射中应用泛型:
Constructor<String> constructor = String.class.getConstructor(StringBuffer.class);
String str = constructor.newInstance(new StringBuffer("abc")); //不需要类型转换
4、总结在哪里可以使用泛型:可以查JDK帮助文档
Class Class<T>、Class Constructor<T>、Class ArrayList<E> :凡是支持泛型的类都可以使用:<T>
23.泛型的内部原理及更深应用
1、泛型对类的约束只在javac编译器进行编译时起作用,编译完成后,类字节码中不再带有泛型类型。
ArrayList<Integer> collection = new ArrayList<Integer>();
//collection.add("abc"); //错误的代码
collection.getClass().getMethod("add", Object.class).invoke(collection,"abc");
System.out.println(collection3.get(0)); //打印结果为:abc
2、泛型类型相关术语:ArrayList<E>类定义和ArrayList<Integer>类引用:
整个称为ArrayList<E> 泛型类型
ArrayList<E> 中的 E 称为类型变量或类型参数
ArrayList<Integer> 称为 参数化的类型
ArrayList<Integer> 中的 Integer 称为类型参数的实例 或 实际类型参数
ArrayList<Integer> 中的 <> 可念成 type of
ArrayList 称为 原始类型
3、参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报警告
Collection<String> c=new vector();//可不可以就是编译器的问题。因为编译后两个类型是兼容的
原始类型可以引用一个参数化类型的对象编译报告警告
Collection c = new Vector<String>();
4、参数化类型不考虑类型的继承关系
Vector<String> v = new Vector<Object>(); //错误
Vector<Object> v = new Vector<String>(); //也是错误的
/***
Vector v1=new Vector<String>();
Vector<Object> v=v1; //这两行代码在编译的时候不会报错。***\
24.泛型的通配符扩展应用
1、设计一个方法,打印任意一个泛型集合的各个元素
public void printCollection(Collection<?> collection)
{
//collection.add("abc"); //错误,在使用通配符泛型时不能调用涉及参数化类型的方法
System.out.println(collection.size()); //正确,因为size()方法不涉及参数化类型
for(Object obj : collection)
{
System.out.println(obj);
}
collection = new HashSet<Date>(); //可以这样指定引用
}
总结:使用 ? 通配符可以引用其他各种参数化的类型,? 通配符定义的变量主要用做引用,可以调用与参数化无关的方法,不能调用与参数有关的方法。
2、限定通配符的上下边界:限定通配符总是包括自己
正确:Vectot<? extends Number> x = new Vector<Integer>(); //上边界
错误:Vectot<? extends Number> x = new Vector<String>();
正确:Vectot<? super Integer> x = new Vector<Number>(); //下边界
错误:Vectot<? super Integer> x = new Vector<Byte>();
3、
Class<?> x;
Class<String> y;
x=y; //表达式正确;y=x; 表达式错误。
25..泛型集合的综合应用案例
HashMap<String,Integer> maps = new HashMap<String,Integer>();
maps.put("abc",123);
maps.put("xyz",321);
Set<Map.Entry<String,Integer>> entrySet=maps.entrySet();
for(Map.Entry<String,Integer> entry:entrySet)
{
System.out.println(entry.getKey() + ":" + entry.getValue());
}
26.自定义泛型类的应用
泛型类示例:车和房子都有品牌,名字和价钱,都是商品或者货物这种数据结构,一般需要获取品牌,名字和价钱的描述信息. 我就将货物定义为泛型类,获取描述信息就是泛型类里面的通用方法.
1.创建HouseBean.java
public class HouseBean
{
private String brand;
private String name;
private String price;
//省略了set/get方法
}
2.创建CarBean.java
public class CarBean
{
private String brand;
private String name;
private String price;
//省略了set/get方法
}
3.创建GenericGoods.java
Goods的泛型类也定义出来,就是类名后面加个<T>, 他的主要功能就是获取泛型实例化的类型,并返回描述信息.
setData 方法就是将实例化对象的信息设置下, 然后在泛型类的方法中进行规整(当然实际应用的时候是可以先做查询数据库等分析,然后给出完整描述,例如售后服务,品牌推广等信息); getClassType方法就是范围实例化对象的类型了, 主要是方便体验.
示例代码:
- public class GenericGoods<T>
- {
- private T t;
- private String information;
- /**
- * Description: default constructor. To get an object of the generic class
- */
- public GenericGoods(T oT)
- {
- this .t = oT;
- }
- /**
- * setData()方法
- */
- public void setData(String sBrand, String sName, String sPrice)
- {
- this .information = "This " + sName + " of " + sBrand + " costs "
- + sPrice + "!" ;
- }
- public String getClassType()
- {
- return t.getClass().getName();
- }
- //省略了set/get方法
- }
4.创建测试类Mymain
- public class MyMain
- {
- public static void main(String[] args) throws SecurityException,
- IllegalArgumentException, NoSuchMethodException,
- IllegalAccessException, InvocationTargetException
- {
- // Car bean generic class test
- GenericGoods<CarBean> oGGCar = new GenericGoods<CarBean>(new CarBean());
- oGGCar.setData("Mercedes", "Benz", "666,000 RMB");
- System.out.println("CarBean test: Type of class - "
- + oGGCar.getClassType() + "; Information of the goods: "
- + oGGCar.getInformation());
- // House bean generic class test
- GenericGoods<HouseBean> oGGHouse = new GenericGoods<HouseBean>(
- new HouseBean());
- oGGHouse.setData("Shenzhen Wanke City",
- "3 rooms with 3 restrooms house", "2,000,000 RMB");
- System.out.println("HouseBean test: Type of class - "
- + oGGHouse.getClassType() + "; Information of the goods: "
- + oGGHouse.getInformation());
- }
- }
运行结果:
1.CarBean test: Type of class - com.ross.generic.bean.CarBean; Information of the goods: This Benz of Mercedes costs 666,000 RMB!
2.HouseBean test: Type of class - com.ross.generic.bean.HouseBean; Information of the goods: This 3 rooms with 3 restrooms house of Shenzhen Wanke City costs 2,000,000 RMB!
27.通过反射获得泛型的实际类型参数
java 的泛型,只是编译时作为类型检查,一旦编译完成,泛型就会被擦除,在运行期间是得不到泛型的信息的,包括它的类型参数。有时候我们需要用到泛型的类型参数,反射看起来是取不到的,因反射在运行期间执行,但那时已无泛型的信息。一些构架却办到了,那么它们是怎么实现的呢?请看下面代码:
<span style="font-size:12px;"><strong>import java.lang.reflect.*;
import java.util.*;
class Test {
public static void main(String args[]) throws Exception {
Method applyMethod=Test.class.getMethod("applyVector", Vector.class);
Type[] types=applyMethod.getGenericParameterTypes();
System.out.println(types[0].toString());
}
public static void applyVector(Vector<Date> v1){
}
}</strong></span>
看到了,实际上是通过获得方法,再取得方法的类型参数,这样泛型的实际类型参数就出来了。上面代码输出:java.util.Vector<java.util.Date>,如果不只一个参数,要具体的类型参数呢?
- import java.lang.reflect.*;
- import java.util.*;
- class Test {
- public static void main(String args[]) throws Exception {
- Method applyMethod=Test.class.getMethod("applyVector", Vector.class);
- Type[] types=applyMethod.getGenericParameterTypes();
- ParameterizedType pType=(ParameterizedType)types[0];
- System.out.println(pType.getActualTypeArguments()[0]);
- System.out.println(pType.getRawType());
- }
- public static void applyVector(Vector<Date> v1){
- }
-
}
输出:
class java.util.Date
class java.util.Vector
28.类加载器
1.类的加载过程
JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)链接又分为三个步骤,如下图所示:
1) 装载:查找并加载类的二进制数据;
2)链接:
验证:确保被加载类的正确性;
准备:为类的静态变量分配内存,并将其初始化为默认值;
解析:把类中的符号引用转换为直接引用;
3)初始化:为类的静态变量赋予正确的初始值;
那为什么我要有验证这一步骤呢?首先如果由编译器生成的class文件,它肯定是符合JVM字节码格式的,但是万一有高手自己写一个class文件,让JVM加载并运行,用于恶意用途,就不妙了,因此这个class文件要先过验证这一关,不符合的话不会让它继续执行的,也是为了安全考虑吧。
准备阶段和初始化阶段看似有点牟盾,其实是不牟盾的,如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析(后面在说),到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。
2. 类的初始化
类什么时候才被初始化:
1)创建类的实例,也就是new一个对象
2)访问某个类或接口的静态变量,或者对该静态变量赋值
3)调用类的静态方法
4)反射(Class.forName("com.lyj.load"))
5)初始化一个类的子类(会首先初始化子类的父类)
6)JVM启动时标明的启动类,即文件名和类名相同的那个类
只有这6中情况才会导致类的类的初始化。
类的初始化步骤:
1)如果这个类还没有被加载和链接,那先进行加载和链接
2)假如这个类存在直接父类,并且这个类还没有被初始化(注意:在一个类加载器中,类只能初始化一次),那就初始化直接的父类(不适用于接口)
3)加入类中存在初始化语句(如static变量和static块),那就依次执行这些初始化语句。
3.类的加载
类的加载的最终产品是位于堆区中的Class对象
Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口
加载类的方式有以下几种:
1)从本地系统直接加载
2)通过网络下载.class文件
3)从zip,jar等归档文件中加载.class文件
4)从专有数据库中提取.class文件
5)将Java源文件动态编译为.class文件(服务器)
4.加载器
JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:
1)Bootstrap ClassLoader
负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类
2)Extension ClassLoader
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
3)App ClassLoader
负责记载classpath中指定的jar包及目录中class
4)Custom ClassLoader
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader
加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
29.代理类
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
一般用接口来引用其子类,如:Collectioncoll = new ArrayList();
AOP
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
安全,事务,日志等功能要贯穿到好多个模块中,所以,它们就是交叉业务
重要原则:不要把供货商暴露给你的客户
1、要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
2、JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
3、JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
4、CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
5、代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1)在调用目标方法之前
2)在调用目标方法之后
3)在调用目标方法前后
4)在处理目标方法异常的catch块中
30.创建和启动线程的两种传统方式两种创建线程的方式:
第一种方式:使用Runnable接口创建线程
1.定义一个Runnable接口类。
2.在此接口类中定义一个对象作为参数run()方法。
3.在run()方法中定义线程的操作。
4.在其它类的方法中创建此Runnable接口类的实例对象,并以此实例对象作为参数创建线程类对象。
5.用start()类方法启动线程。
示例:
public class MyThread implements Runnable //使用Runnable接口方法创建线程和启动线程。 { int count=1,number; public MyThread(int num) { number=num; System.out.println("创建线程"+number); } public void run() { while(true) { System.out.println("线程"+number+":计数"+count); if(++count==3) return; } } public static void main (String args[] ) { for(int i=0;i<2;i++)( new Thread(new MyThread(i+1)).start(); //启动线程 } }
第二种方式:直接继承Thread类创建对象
(1)定义线程类:
public class 线程类名 extends Thread {
……
public void run(){
…… //编写线程的代码
}
}
(2)创建线程类对象
(3)启动线程: 线程类对象.start();
31.传统的定时器实现方式
方法1:创建一个Timer,用于在间隔时间后调用ResultSetsql()方法
在switch值发生改变的时候对Timer进行操作
值变为0时调用timer.cancel();
值为1时调用
timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
ResultSetsql();
}
}, repeattime);
方法2:建立一个线程,每隔一秒进行循环,检查monitor switch的状态
new Thread(new Runnable() {
@Override
public void run() {
while(isRun){
if(monitor.switch == 1){
ResultSetsql();
try {
Thread.sleep(repeattime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
32.传统的线程同步互斥
同步的概念再于线程间通信,比较典型的例子就是“生产者-消费者问题”。
多个生产者和多个消费者就是多条执行线程,他们共同操作一个数据结构中的数据,数据结构中有时是没有数据的,这个时候消费者应该处于等待状态而不是不断的去访问这个数据结构。这里就涉及到线程间通信(当然此处还涉及到互斥,这里暂不考虑这一点),消费者线程一次消费后发现数据结构空了,就应该处于等待状态,生产者生产数据后,就去唤醒消费者线程开始消费。生产者线程某次生产后发现数据结构已经满了,也应该处于等待状态,消费者消费一条数据后,就去唤醒生产者继续生产。
解决办法:实现这种线程间同步,可以通过Object类提供的wait,notify, notifyAll 3个方法去进行即可。
线程间互斥应对的是这种场景:多个线程操作同一个资源(即某个对象),为保证线程在对资源的状态(即对象的成员变量)进行一些非原子性操作后,状态仍然是正确的。典型的例子是“售票厅售票应用”。售票厅剩余100张票,10个窗口去卖这些票。这10个窗口,就是10条线程,售票厅就是他们共同操作的资源,其中剩余的100张票就是这个资源的一个状态。线程买票的过程就是去递减这个剩余数量的过程。
33.传统的线程间通讯
Object类有三个特殊的方法,分别是 wait(),notify(),notifyAll()这3个方法,他们不属于Thread,但和Thread却是息息相关.他们3个必须是由同步监视器对象来调用.
- 对于使用synchronized修饰的同步方法,因为该类的默认实例(this)就是同步监视器,所以可以在同步方法总直接调用3个方法.
- 对于使用synchronized修饰的同步代码块,同步监视器是synchronized后括号里的对象,就要用这个对象来调用.
- wait():导致当前线程等待,直接其他线程用notify或者notifyAll唤醒.wait有三种1.无时间的,会一直等待.2.两种带时间的.
- notify():唤醒在此同步监视器上等待的某个单个线程.选择是随意性的.
- notifyAll():唤醒所有在此同步监视器上等待的线程.
34.线程池
1.概述:
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。2.组成部分
.线程池管理器(ThreadPoolManager):用于创建并管理线程池 .工作线程(WorkThread): 线程池中线程 .任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。 .任务队列:用于存放没有处理的任务。提供一种缓冲机制。3.范围
(1)需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。 (2)对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。 (3)接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,并出现"OutOfMemory"的错误。4.线程池示例
// 线程池示例 using System; using System.Threading; public class Test { // 存放要计算的数值的字段 static double number1 = -1; static double number2 = -1; public static void Main() { // 获取线程池的最大线程数和维护的最小空闲线程数 int maxThreadNum, portThreadNum; int minThreadNum; ThreadPool.GetMaxThreads( out maxThreadNum, out portThreadNum); ThreadPool.GetMinThreads( out minThreadNum, out portThreadNum); Console.WriteLine( "最大线程数:{0}" , maxThreadNum); Console.WriteLine( "最小线程数:{0}" , minThreadNum); // 函数变量值 int x = 15600; // 启动第一个任务:计算x的8次方 Console.WriteLine( "启动第一个任务:计算{0}的8次方。" , x); ThreadPool.QueueUserWorkItem( new WaitCallback(TaskProc1), x); // 启动第二个任务:计算x的8次方根 Console.WriteLine( "启动第二个任务:计算{0}的8次方根。" , x); ThreadPool.QueueUserWorkItem( new WaitCallback(TaskProc2), x); // 等待,直到两个数值都完成计算 while (number1 == -1 || number2 == -1) ; // 打印计算结果 Console.WriteLine( "y({0}) = {1}" , x, number1 + number2); Console.Read(); } // 启动第一个任务:计算x的8次方 static void TaskProc1( object o) { number1 = Math.Pow(Convert.ToDouble(o), 8); } // 启动第二个任务:计算x的8次方根 static void TaskProc2( object o) { number2 = Math.Pow(Convert.ToDouble(o), 1.0 / 8.0); } } |
35.锁与读写锁
读写访问资源的条件
读取: 没有线程正在做写操作,且没有线程在请求写操作。
写入 :没有线程正在做读写操作。
如果某个线程想要读取资源,只要没有线程正在对该资源进行写操作且没有线程请求对该资源的写操作即可。我们假设对写操作的请求比对读操作的请求更重要,就要提升写请求的优先级。此外,如果读操作发生的比较频繁,我们又没有提升写操作的优先级,那么就会产生“饥饿”现象。请求写操作的线程会一直阻塞,直到所有的读线程都从ReadWriteLock上解锁了。如果一直保证新线程的读操作权限,那么等待写操作的线程就会一直阻塞下去,结果就是发生“饥饿”。因此,只有当没有线程正在锁住ReadWriteLock进行写操作,且没有线程请求该锁准备执行写操作时,才能保证读操作继续。
当其它线程没有对共享资源进行读操作或者写操作时,某个线程就有可能获得该共享资源的写锁,进而对共享资源进行写操作。有多少线程请求了写锁以及以何种顺序请求写锁并不重要,除非你想保证写锁请求的公平性。
示例:
public class ReadWriteLock{ |
|
private int readers = 0 ; |
|
private int writers = 0 ; |
|
private int writeRequests = 0 ; |
|
|
|
public synchronized void lockRead() |
|
throws InterruptedException{ |
|
while (writers > 0 || writeRequests > 0 ){ |
|
wait(); |
|
} |
|
readers++; |
|
} |
|
public synchronized void unlockRead(){ |
|
readers--; |
|
notifyAll(); |
|
} |
|
public synchronized void lockWrite() |
|
throws InterruptedException{ |
|
writeRequests++; |
|
|
|
while (readers > 0 || writers > 0 ){ |
|
wait(); |
|
} |
|
writeRequests--; |
|
writers++; |
|
} |
|
|
|
public synchronized void unlockWrite() |
|
throws InterruptedException{ |
|
writers--; |
|
notifyAll(); |
|
} |
|
} |
36.Semaphore同步工具
Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中在等待的另外5个人中又有一个可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。
Semaphore在java.util.concurrent包下, 从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个acquire()
,然后再获取该许可。每个release()
添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore
只对可用许可的号码进行计数,并采取相应的行动。说白了,Semaphore 是一个计数器,在计数器不为 0 的时候对线程就放行,一旦达到 0,那么所有请求资源的新线程都会被阻塞,包括增加请求到许可的线程,也就是说Semaphore不是可重入的。每一次请求一个许可都会导致计数器减少 1,同样每次释放一个许可都会导致计数器增加 1,一旦达到了 0,新的许可请求线程将被挂起。
构造方法:
<span style="font-size:12px;">public Semaphore(int permits)创建具有给定的许可数和非公平的公平设置的 Semaphore。
public Semaphore(int permits,
boolean fair)创建具有给定的许可数和给定的公平设置的 Semaphore。</span>
<span style="font-size:12px;"></span>
主要方法:
1.void acquire()
从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
2.void acquire(int permits)
从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。
3. void release()
释放一个许可,将其返回给信号量。
4. void release(int permits)
释放给定数目的许可,将其返回到信号量。
37.CountDownLatch同步工具
CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
主要方法
public CountDownLatch(int count);
public void countDown();
public void await() throws InterruptedException
构造方法参数指定了计数的次数
countDown方法,当前线程调用此方法,则计数减一
awaint方法,调用此方法会一直阻塞当前线程,直到计时器的值为0
public class CountDownLatchDemo {
final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch=new CountDownLatch(2);//两个工人的协作
Worker worker1=new Worker("zhang san", 5000, latch);
Worker worker2=new Worker("li si", 8000, latch);
worker1.start();//
worker2.start();//
latch.await();//等待所有工人完成工作
System.out.println("all work done at "+sdf.format(new Date()));
}
static class Worker extends Thread{
String workerName;
int workTime;
CountDownLatch latch;
public Worker(String workerName ,int workTime ,CountDownLatch latch){
this.workerName=workerName;
this.workTime=workTime;
this.latch=latch;
}
public void run(){
System.out.println("Worker "+workerName+" do work begin at "+sdf.format(new Date()));
doWork();//工作了
System.out.println("Worker "+workerName+" do work complete at "+sdf.format(new Date()));
latch.countDown();//工人完成工作,计数器减一
}
private void doWork(){
try {
Thread.sleep(workTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出:
Worker zhang san do work begin at 2014-06-30 03:13:11
Worker li si do work begin at 2014-06-30 03:13:11
Worker zhang san do work complete at 2014-06-30 03:13:11
Worker li si do work complete at 2014-06-30 03:13:11
all work done at 2014-06-30 03:13:11
38.Exchanger同步与数据交换工具
示例:
public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); final Exchanger exchanger = new Exchanger(); service.execute(new Runnable() { public void run() { try { String data1 = "money"; System.out.println("线程" + Thread.currentThread().getName() + "正在把数据" + data1 + "换出去"); Thread.sleep((long) (Math.random() * 10000)); String data2 = (String) exchanger.exchange(data1); System.out.println("线程" + Thread.currentThread().getName() + "换回的数据为" + data2); } catch (Exception e) { } } }); service.execute(new Runnable() { public void run() { try { String data1 = "drugs"; System.out.println("线程" + Thread.currentThread().getName() + "正在把数据" + data1 + "换出去"); Thread.sleep((long) (Math.random() * 10000)); String data2 = (String) exchanger.exchange(data1); System.out.println("线程" + Thread.currentThread().getName() + "换回的数据为" + data2); } catch (Exception e) { } } }); } 运行的结果如下所示:线程pool-1-thread-1正在把数据money换出去
线程pool-1-thread-2正在把数据drugs换出去
线程pool-1-thread-1换回的数据为drugs
线程pool-1-thread-2换回的数据为money
39.可阻塞队列的实现
示例:
public class BlockingQueue {
|
02 |
|
03 |
private List queue = new LinkedList();
|
04 |
|
05 |
private int limit = 10 ;
|
06 |
|
07 |
public BlockingQueue( int limit){
|
08 |
|
09 |
this .limit = limit;
|
10 |
|
11 |
} |
12 |
|
13 |
public synchronized void enqueue(Object item)
|
14 |
|
15 |
throws InterruptedException {
|
16 |
|
17 |
while ( this .queue.size() == this .limit) {
|
18 |
|
19 |
wait(); |
20 |
|
21 |
} |
22 |
|
23 |
if ( this .queue.size() == 0 ) {
|
24 |
|
25 |
notifyAll(); |
26 |
|
27 |
} |
28 |
|
29 |
this .queue.add(item);
|
30 |
|
31 |
} |
32 |
|
33 |
public synchronized Object dequeue()
|
34 |
|
35 |
throws InterruptedException{
|
36 |
|
37 |
while ( this .queue.size() == 0 ){
|
38 |
|
39 |
wait(); |
40 |
|
41 |
} |
42 |
|
43 |
if ( this .queue.size() == this .limit){
|
44 |
|
45 |
notifyAll(); |
46 |
|
47 |
} |
48 |
|
49 |
return this .queue.remove( 0 );
|
50 |
|
51 |
} |
52 |
|
53 |
} |
------- android培训、java培训、期待与您交流! ----------