UDP socket
这种信息传输方式相当于传真,信息打包,在接受端准备纸。
先由客户端给服务器发消息。以告诉服务器客户端的地址。
特点:
1) 基于UDP无连接协议
2) 不保证消息的可靠传输
3) 它们由Java技术中的DatagramSocket和DatagramPacket类支持
DatagramSocket(邮递员):对应数据报的Socket概念,不需要创建两个socket,不可使用输入输出流。
DatagramPacket(信件):数据包,是UDP下进行传输数据的单位,数据存放在字节数组中,其中包括了目标地址和端口以及传送的信息(所以不用建立点对点的连接)。
DatagramPacket的分类:
用于接收:DatagramPacket(byte[] buf,int length)
DatagramPacket(byte[] buf,int offset,int length)
用于发送:DatagramPacket(byte[] buf,int length, InetAddress address,int port )
DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port)
注:InetAddress类网址用于封装IP地址
没有构造方法,通过
InetAddress.getByAddress(byte[] addr):InetAddress
InetAddress.getByName(String host):InetAddress
等。
建立UDP 发送端
创建一个UDP的客户端的程序的步骤:
1). 创建一个DatagramPacket,其中包含发送的数据和接收方的IP地址和端口
号。
2). 创建一个DatagramSocket,其中包含了发送方的IP地址和端口号。
3). 发送数据
4). 关闭DatagramSocket
byte[] buf = new byte[1024];
DatagramSocket datagramSocket = new DatagramSocket(13);// set port
DatagramPackage intputPackage = new DatagramPackage(buf,buf.length);
datagramSocket.receive(inputPackage);
DatagramPackage outputPackage = new DatagramPackage(buf,0,buf.length,inetAddress,port);
datagramSocket.send(outputPackage); //客户端向服务器发信的过程
没建立流所以不用断开。
建立UDP 接受端
创建一个UDP的服务器端的程序的步骤:
1). 创建一个DatagramPacket,用于存储发送方发送的数据及发送方的IP地址和端口号。
2). 创建一个DatagramSocket,其中指定了接收方的IP地址和端口号。
3). 接收数据
4). 关闭DatagramSocket
byte[] buf = new byte[1024];
DatagramSocket datagramSocket = new DatagramSocket();//不用设端口,因为发送的包中端口
DatagramPackage outputPackage=new DatagramPackage(Buf,buf.length,serverAddress,serverPort);
DatagramPackage inputPackage=new DatagramPackage(buf,0,buf.length); //收信之前要准备一封空信
datagramSocket.receive(inputPackage);
UDP处理多客户端,不必多线程,回信可以一封一封的回
在收发信设置为死循环
java.net.URL 统一资源定位器
定位在internet上的某一资源
更高层的网络传输
常用格式
协议名://主机名:端口号/定位寻找主机内部的某一资源
http://www.tarena.com.cn:80/bin/index.html
作用:
1、必须完成
网络聊天室,以广播的形式传输数据
2、QQ聊天室
实现点对点的数据传输
3、统计一个目录下的所有“.java”文件及子目录下的".java"文件总共多少行
CoreJava第十七天 2007-5-21
一、Java5.0新特性
1、编译器的功能更加强大,JVM变化不大
2、Java在逐渐与C++融合
3、五小点,四大点
二、五小点
1、自动封箱AutoBoxing/自动解封
自动封箱和自动拆箱,它实现了简单类型和封装类型的相互转化时,实现了自动转化。
byte b -128~127
Byte b 在以上数量的基础上多一个null
简单类型和封装类型之间的差别
封装类可以等于null ,避免数字得时的二义性。
Integer i=null;
int ii=i; //会抛出NullException 异常。相当于 int ii=i.intValue();
Integer i=1; //相当于Integer i=new Integer(1);
i++; // i = new Integer(i.intValue()+1);
在基本数据类型和封装类之间的自动转换
5.0之前
Integer i=new Integer(4);
int ii= i.intValue();
5.0之后
Integer i=4;
Long l=4.3;
public void m(int i){......}
public void m(Integer i){......}
以上两个函数也叫方法重载
自动封箱解箱只在必要的时候才进行。能不封箱找到匹配的就不封箱。
2、静态引入 StaticImport
使用类中静态方法时不用写类名
System.out.println(Math.round(PI));
可以用以下代码实现:
import static java.lang.System.*; //注意,要写" 类名.* "
import static java.lang.Math.*;
out.println(round(PI));
注意:静态引入的方法不能重名
3、for-each
统一了遍历数组和遍历集合的方式
for(Object o:list){ //Object o 表示每个元素的类型 ,list 表示要遍历的数组或集合的名字
System.out.println(o); //打印集合或数组中的每个元素
}
4、可变长参数
处理方法重载中,参数类型相同,个数不同的情况
public void m(int... is){.....}
int... is 相当于一个 int[] is
编译器会把给定的参数封装到一个数组中,再传给方法
在一个方法中只能有一个可变长参数,而且,必须放在最后一个参数的位置
5、格式化输入/输出
java.util.Formatter类 对格式的描述
System.out.printf("Hello %s",str); //打印字符串类型的变量,用一个占位符
格式化I/O(Formatted I/O)
java.util.Sacner类可以进行格式化的输入,可以使用控制台输入,结合了BufferedReader和StringTokener的功能。
三、四大点
1、枚举
枚举是一个类,并且这个类的对象是现成的,在定义类的时候,即定义好了对象
程序员要使用的时候,只能从中选择,无权创建
enum 枚举名{
枚举值1(..),枚举值2(..),.....;
}
(1) 在5.0之前使用模式做出一个面向对象的枚举
final class Season{
public static final Season SPRING=new Season();
public static final Season WINTER=new Season();
public static final Season SUMMER=new Season();
public static final Season AUTUMN=new Season();
private Season(){}
}
完全等价于
enum Season2{
SPRING(..),//枚举值
SUMMER(..),
AUTUMN(..),
WINTER(..)
}
枚举本质上也是一个类,Enum是枚举的父类。
这个类编译以后生成一个.class类
这个类有构造方法,但是是私有的
枚举中的values()方法会返回枚举中的所有枚举值
枚举中可以定义方法和属性,最后的一个枚举值要以分号和类定义分开,枚举中可以定义的构造方法。
枚举不能继承类(本身有父类),但可以实现接口,枚举不能有子类也就是final的,枚举的构造方法是private(私有的)。
枚举中可以定义抽象方法,可以在枚举值的值中实现抽象方法。
枚举值就是枚举的对象,枚举默认是final,枚举值可以隐含的匿名内部类来实现枚举中定义抽象方法。
(2)枚举类(Enumeration Classes)和类一样,具有类所有特性。Season2的父类是java.lang.Enum;
隐含方法: 每个枚举类型都有的方法。
Season2[] ss=Season2.values(); ----获得所有的枚举值
for(Season2 s:ss){
System.out.println(s.name()); ----- 打印枚举值
System.out.println(s.ordinal()); ----- 打印枚举值的编号
}
(3) enum可以switch中使用(不加类名)。
switch( s ){
case SPRING:
…………….
case SUMMER:
…………….
…………..
}
(4)枚举的有参构造
enum Season2{
SPRING(“春”),-------------------------------逗号
SUMMER(“夏”),-------------------------------逗号
AUTUMN(“秋”),-------------------------------逗号
WINTER(“冬”);-------------------------------分号
private String name;
Season2(String name){ //构造方法必须是私有的,可以不写private,默认就是私有的
this.name=name;
}
String getName(){
return name;
}
}
Season2.SPRING.getName() ---------------------春
(5)枚举中定义的抽象方法,由枚举值实现:
enum Operation{
ADD('+'){
public double calculate(double s1,double s2){
return s1+s2;
}
},
SUBSTRACT('-'){
public double calculate(double s1,double s2){
return s1-s2;
}
},
MULTIPLY('*'){
public double calculate(double s1,double s2){
return s1*s2;
}
},
DIVIDE('/'){
public double calculate(double s1,double s2){
return s1/s2;
}
};
char name;
public char getName(){
return this.name;
}
Operation(char name){
this.name=name;
}
public abstract double calculate(double s1 ,double s2);
}
有抽象方法枚举元素必须实现该方法。
Operator[] os = Operator.values();
for(Operator o:os){
System.out.println("8 "+o.name()+" 2="+o.calculate(8,2));
}
for(Operator o:os){
System.out.println("8 "+o.getName()+" 2="+o.calculate(8,2));
}
运行结果:
8 ADD 2=10.0
8 SUBSTRACT 2=6.0
8 MULTIPLY 2=16.0
8 DIVIDE 2=4.0
8 + 2=10.0
8 - 2=6.0
8 * 2=16.0
8 / 2=4.0
2、泛型
(1)增强了java的类型安全,可以在编译期间对容器内的对象进行类型检查,在运行期不必进行类型的转换。
而在java se5.0之前必须在运行期动态进行容器内对象的检查及转换,泛型是编译时概念,运行时没有泛型
减少含糊的容器,可以定义什么类型的数据放入容器
(2)List<Integer> aList = new ArrayList<Integer>();
aList.add(new Integer(1));
// ...
Integer myInteger = aList.get(0); //从集合中得到的元素不必强制类型转换
支持泛型的集合,只能存放制定的类型,或者是指定类型的子类型。
HashMap<String,Float> hm = new HashMap<String,Float>();
不能使用原始类型
GenList<int> nList = new GenList<int>(); //编译错误
编译类型的泛型和运行时类型的泛型一定要一致。没有多态。
List<Dog> as = new ArrayList<Dog>();
List<Animal> l = as; //error Animal与Dog的父子关系不能推导出List<Animal> 与 List<Dog> 之间的父子类关系
(3)泛型的通配符"?"
? 是可以用任意类型替代。
<?> 泛型通配符表示任意类型
<? extends 类型> 表示这个类型是某个类型或接口的子类型。
<? super 类型> 表示这个类型是某个类型的父类型。
import java.util.*;
import static java.lang.System.*;
public class TestTemplate {
public static void main(String[] args) {
List<Object> l1=new ArrayList<Object>();
List<String> l2=new ArrayList<String>();
List<Number> l3=new ArrayList<Number>(); //Number --- Object的子类,所有封装类的父类
List<Integer> l4=new ArrayList<Integer>();
List<Double> l5=new ArrayList<Double>();
print(l1);
print(l2);
print(l3);
print(l4);
print(l5);
}
static void print(List<? extends Number> l){ //所有Number及其子类 l3,l4,l5通过
for(Number o:l){
out.println(o);
}
}
static void print(List<? extends Comparable> l){……} //任何一个实现Comparable接口的类 l2,l4,l5通过
static void print(List<? super Number> l){……} //所有Number及其父类 l1,l3通过
// "?"可以用来代替任何类型, 例如使用通配符来实现print方法。
public static void print(GenList<?> list){……} //表示任何一种泛型
}
(4)泛型方法的定义 --- 相当于方法的模版
把数组拷贝到集合时,数组的类型一定要和集合的泛型相同。
<...>定义泛型,其中的"..."一般用大写字母来代替,也就是泛型的命名,其实,在运行时会根据实际类型替换掉那个泛型。
在方法的修饰符和返回值之间定义泛型
<E> void copyArrayToList(E[] os,List<E> lst){……}
static <E extends Number & Comparable> void copyArrayToList(E[] os,List<E> lst){……} //定义泛型的范围 类在前接口在后
static<E , V extends E> void copyArrayToList(E[] os,List<E> lst){……} //定义多个泛型
"super"只能用在泛型的通配符上,不能用在泛型的定义上
import java.util.*;
public class TestGenerics3 {
public static void main(String[] args) {
List<String> l1=new ArrayList<String>();
List<Number> l2=new ArrayList<Number>();
List<Integer> l3=new ArrayList<Integer>();
List<Double> l4=new ArrayList<Double>();
List<Object> l5=new ArrayList<Object>();
String[] s1=new String[10];
Number[] s2=new Number[10];
Integer[] s3=new Integer[10];
Double[] s4=new Double[10];
Object[] s5=new Object[10];
copyFromArray(l1,s1);
copyFromArray(l2,s2);
copyFromArray(l3,s3);
copyFromArray(l4,s4);
copyFromArray(l5,s5);
}
//把数组的数据导入到集合中
public static <T extends Number&Comparable> void copyFromArray(List<T> l,T[] os){
for(T o:os){
l.add(o);
}
}
}
受限泛型是指类型参数的取值范围是受到限制的. extends关键字不仅仅可以用来声明类的继承关系, 也可以用来声明类型参数(type parameter)的受限关系.
泛型定义的时候,只能使用extends不能使用 super,只能向下,不能向上。
调用时用<?>定义时用 <E>
(5)泛型类的定义
类的静态方法不能使用泛型,因为泛型类是在创建对象的时候产生的。
class MyClass<E>{
public void show(E a){
System.out.println(a);
}
public E get(){
return null;
}
}
受限泛型
class MyClass <E extends Number>{
public void show(E a){
}
}
3、注释
4、并发
四、反射 reflect
反射,在运行时,动态分析或使用一个类进行工作。
反射是一套API,是一种对底层的对象操作技术
1、类加载
类加载,生成.class文件,保存类的信息
类对象,是一个描述这个类信息的对象,对虚拟机加载类的时候,就会创建这个类的类对象并加载该对象。
Class,是类对象的类。称为类类。只有对象才会被加载到虚拟机中。一个类只会被加载一次。
2、获得类对象的三种方式:(类对象不用new的方法得到的)
1)也可以用 类名.Class,获得这个类的类对象。
2)用一类的对象掉用a.getClass(),得到这个对象的类型的类对象。
3)也可以使用Class.forName(类名)(Class类中的静态方法),也可以得到这个类的类对象,
(注意,这里写的类名必须是全限定名(全名),是包名加类名,XXX.XXX.XXXX)。强制类加载,这种方法是经常使用的。
一个类的类对象是唯一的。
在使用Class.forName(类名)时,如果使用时写的类名的类,还没有被加载,则会加载这个类。
Class c;
c.getName(); 返回类名
c.getSuperclass(); 这个方法是获得这个类的父类的类对象。
c.getInterfaces(); 会获得这个类所实现的接口,这个方法返回是一个类对象的数组。
方法对象是类中的方法的信息的描述。java.lang.reflect.Method,方法类的对象可以通过类对象的getMethods() 方法获得,
获得的是一个方法对象的数组,获得类中的定义的所有方法对象,除了构造方法。
构造方法对象,是用来描述构造方法的信息。java.lang.reflect.Constructor构造方法类的对象可以通过类对象的getConstructors()方法获得,
获得这个类的所有构造方法对象。
属性对象,使用来描述属性的信息。java.lang.reflect.Field属性类的对象对象可以通过类对象getFields() 这个方法是获得所有属性的属性对象。
作业:
1、通过运行时命令行参数输入一个类名,类出类中所有的方法
2、实现一个带泛型的堆栈,用来存放Number
3、实现一个栈,用来存放任意类型
方法:遍历,pop,push,从数组拷贝
4、定义一个枚举,枚举值是课程,每个枚举值有个教师姓名的属性
CoreJava第十八天 2007-5-22
一、反射
1、获取方法和属性
反射可以获取这个类中定义的方法和属性的信息,简单数据类型在使用反射时要转换成封装类。
Class c = Teacher.class;
Method m = c.getMethod("mrthod",int.class);
2、通过类对象生成类的对象
Class c = Teacher.class;
Object o = c.newInstance();
3、通过类对象调用方法
//1.get class Object
Class c=Class.forName("Student");
//2.get Constructor object
Class[] cs={String.class};
Constructor con=c.getConstructor(cs);//按照参数表来调用制定构造方法。
//3.create object
Object[] os={"liucy"};
Object o=con.newInstance(os);
//4.get method object
String methodName="study";
Class[] pcs={String.class};
Method m=c.getMethod(methodName,pcs);//按照参数表来获得制定的方法对象。
//5.invoke the method
Object[] ocs={"EJB"};
m.invoke(o,ocs);
二、CoreJava 5.0的注释
1、定义:Annotation描述代码的代码(区:描述代码的文字)
给机器看 给人看的
2、注释的分类:
(1)、标记注释:没有任何属性的注释。@注释名
(2)、单值注释:只有一个属性的注释。@注释名(value=___)
在单值注释中如果只有一个属性且属性名就是value,则“value=”可以省略。
(3)、多值注释:有多个属性的注释。多值注释又叫普通注释。
@注释名(多个属性附值,中间用逗号隔开)
3、内置注释:
(1)、Override(只能用来注释方法)
表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息。
(2)、Deprecated
用 @Deprecated 注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告。
(3)、SuppressWarnings(该注释无效)
指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。
4、自定义注释
自定义注释
public @interface Test{
}
在自定义注释时,要用注释来注释(描述)注释。
@target(),用来描述(注释)注释所能够注释的程序员元素。
@Retention(),描述(注释)注释要保留多久。
注释的属性类型可以是
8种基本类型
String
Enum
Annotation
以及它们的数组
5、注释的注释:java.lang. annotation包中
(1)、Target:指示注释类型所适用的程序元素的种类。
例:@Target(value = {ElementType.METHOD});
说明该注释用来修饰方法。
(2)、Retention:指示注释类型的注释要保留多久。如果注释类型声明中不存在 Retention 注释,则保留策略默认为RetentionPolicy.CLASS。
例:Retention(value = {RetentionPolicy.xxx}
当x为CLASS表示保留到类文件中,运行时抛弃。
当x为RUNTIME表示运行时仍保留
当x为SOURCE时表示编译后丢弃。
(3)、Documented:指示某一类型的注释将通过 javadoc 和类似的默认工具进行文档化。应使用此类型来注释这些类型的声明:其注释会影响由其客户端注释的元素的使用。
(4)、Inherited:指示注释类型被自动继承。如果在注释类型声
明中存在 Inherited 元注释,并且用户在某一类声明中查询该注释类型,同时该类声明中没有此类型的注释,则将在该类的超类中自动查询该注释类型。
注:在注释中,一个属性既是属性又是方法。
■ 标注:描述代码的文字
描述代码的代码。给编译器看的代码,作用是规范编译器的语法。
class Student{
@Override
public String toString(){
return “student”;
}
}
■ 注释类型 java.lang
1、标记注释(没有属性)
@Override
2、单值注释
@注释名(a="liucy")
@注释名(prameter=10)
int parameter
特例:
@注释名 (value=“134” )
等价于 @注释名 (“134” )
3.普通注释(多值注释)
(key1=value,……)
@__(a="liucy,b=10)
■ @Override 只能放在方法前面
@Deprecated 用于描述过期
@SuppressWarnings 抑制警告
@SuperessWarning({“ddd”,”aaa”,”ccc”}) //JVM还没有实现这个注释
三、Java5.0中的并发
1、所在的包:Java.util.concurrent
2、重写线程的原因:
(1)何一个进程的创建(连接)和销毁(释放资源)的过程都 是一个不可忽视的开销。
(2)run方法的缺陷:没有返回值,没有抛例外。
3、对比1.4和5.0的线程
5.0 1.4
ExecutorService 取代 Thread
Callable Future 取代 Runnable
Lock 取代 Synchronized
SignalAll 取代 notifyAll()
await() 取代 wait()
三个新加的多线程包
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抛出的异常