Java知识的小汇总

版权声明:本文为博主abcnull的原创文章,未经博主允许不得转载。 https://blog.csdn.net/abcnull/article/details/79548120
jdk文件目录结构:
jdk
 
-bin:存放Java编译器javac.exe,Java运行工具java.exe,Java打包工具jar.exe,还有Java文档生成工具等
-db:小型的数据库,jdk6开始Java引入的新成员javaDB,支持JDBC4.0所有规范
-jre:Java程序运行环境,其中有JVM,一些运行时的类包,Java应用启动器,以及一个bin目录
-include:JDK是通过c/c++实现的,启动时需要引入一些头文件,于是都放在这里
-lib:Java类库
-src.zip:为src文件夹的压缩文件,src放置的是jdk核心类源代码,通过该文件可以查看Java基础类的源代码,如Java一些lang包,util包等。
JDK内部的jre是专用jre,有的人可能外部又安装了个jre,那是共用jre,一般安装了jdk会自带jre不用再安装共用jre。
jre文件
-bin
-lib

Java小细节:
一个Java中只有一个public class,并且public class文件名要跟.Java文件名一致。Java中连续的字符串是不能分开在两行书写的。Java中byte整型1个字节,char是2个字节。Java封装继承多态抽象重载。System.gc()主动调用jvm进行垃圾回收。Java中常用静态代码块对类的成员变量进行初始化。javadoc命令可以将Java中文档注释内容提取出来自动生成HTML格式的帮助文档。Java不允许多重继承,而C++支持多重继承,可是要慎用容易出问题。JDK7.0之后switch语句开始支持字符串类型了。JDK5.0之后增加了一个类StringBuilder类和foreach循环,for(Object obj:list)会将每次list元素赋给obj。泛型JDK5之后采用。

Java运行机制:
首先用javac.exe编译器将java源程序编译成与平台无关的字节码文件,之后由java虚拟机对字节码文件解释执行,在执行字节码时,JVM负责将每一条要执行的字节码送给解释器,解释器再将其翻译成特定平台环境的机器指令并执行。JVM在解释执行字节码文件时还可以优化字节码,使其转换成效率更高的机器指令,Java的跨平台特性是由不同平台不同的JVM决定的。JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,ClassLoader是java运行时一个重要的系统组件,负责在运行时查找和装入类文件的类。

static{}静态代码块和非静态代码块的区别:
静态代码块,在虚拟机加载类的时候就会执行,且执行一次。非静态代码块在创建对象即new一个对象的时候执行,且每次创建对象都会执行一次。所以说有时候有一些项目,在项目启动时就要执行的就可以用static{}。

单例模式:
单例模式需要做到两点:私有化构造方法(防止外部创建实例),静态公有实例化对象,可以被外部访问但无法再修改。
class Single{
	private Single(){} // 私有化构造方法
	public static final Single INSTANCE=new Single(); // 公有静态实例化对象且不能修改	
 }
或者:
class Single{
	private Single(){} // 私有化构造方法
	private static Single INSTANCE=new Single(); // 私有静态实例化对象
	public static Single getInstance(){ // 公有静态方法返回一个单例模式对象,可外部直接访问
		return INSTANCE;
	}
}


内部类:

①内部类:Outer.Inner inner=new Outer().new Inner();
②静态内部类:Outer.Inner inner=new Outer().Inner();
③方法内部类:new Outer().test();

④匿名内部类:匿名内部类并不是实例化对象,而是创建一个继承自Animal的匿名类对象,通过new表达式返回的引用自动向上转型为Animal的引用。

animalShout(new Animal(){
	public void shout(){
		System.out.println("eeee");
	} 
});


Java关于引用的理解:
Person person;

这里的person并不是真正的对象,而是一个指向Person类对象的引用。


Java堆和栈的理解:
①Java堆:Java中的内存区粗略可分为堆内存和栈内存,Java的堆是Java虚拟机所管理的内存中最大的一块,这个内存的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存,堆是被所有线程所共享的一块内存,Java堆可以处于物理上不连续的内存空间,只要逻辑上是连续的就好。引用类型的变量,其内存分配在堆上或者常量池,主要存放运行时创建(new)的对象。
②Java栈:Java栈可不是线程共享的,它是线程私有的,它的生命周期也是和私有线程的生命周期捆绑在一起。基本数据类型 变量以及对象的引用 变量,其内存分配在栈上,变量出了作用域就会自动释放。栈内存的主要作用是存放基本数据类型和引用变量。栈的内存管理是通过栈的"后进先出"模式来实现的。
③那为何要区分栈和堆呢?
当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法定义的变量将会放到这块栈内存里,随着方法的结束,这个方法的内存栈也将自动销毁。(不需要GC回收)

当我们在程序中创建一个对象时,这个对象会被保存到堆内存,以便反复使用,因为创建对象的成本会比较大,堆内存有个特点,他不会随着方法的结束而自动销毁,既是方法结束后,这个存在于堆内存的对象还可以被另一个/对象的引用变量/所引用。之所以说创建对象的开销会很大,是因为创建对象的根本途径是构造器,用new关键字来调用某个构造器完成这个类的实例。


异常概述:

大量的异常类继承自java.lang.Throwable类,该类有两个直接子类一个Error表示运行时的错误,另一个是Exception表示异常,Exception又有其他的子类,RuntimeException类及其子类都是运行时异常,而Exception的其他子类都是编译时就异常。其中Throwable类中常用的方法有:String getMessage()返回此throwable的详细消息字符串;void printStackTrace()将此throwable及其追踪输出至标准错误流;void printStackTrace(PrintStream s)将此throwable及其追踪输出至指定的输出流;
try{
    /*可能出现异常的代码*/
}catch(Exception e){
    /*异常输出*/
}finally{
    /*无论是否发生异常都执行的语句*/
}
对可能发生异常的代码用try...catch进行处理,若在catch代码块中增加了一个return语句,则用于结束当前try...catch所在的方法,但是finally中必须要执行完,所以finally中经常被用作释放系统资源等事情,但有种情况finally不能执行,是try..catch中执行了System.exit(0),表示退出当前的Java虚拟机。在调用别人的方式时为防止不知道的错误可以抛出异常,
public void function() throws Exception{
    /*可能出现异常的语句*/
}
也可以用自定义异常,除了在需要调用的方法后抛出异常,也可在某处抛出自定义的异常,自定义异常可以继承自Exception,而它内部是有带参数和不带参数的构造方法。throwable new DivideByMinusException();一旦抛出异常就必须得捕获,如在方法内某处抛出异常可以在方法后面在抛出异常,然后再调用方法处try...catch一下。

编译异常和运行异常:
编译异常(checked异常):在程序编译时产生的异常,而这些异常必须要进行处理。
运行异常(unchecked异常):在程序运行时产生的异常,这种异常既是不编写异常处理部分也依然可以通过编译。如数组下标越界,除以0等产生的运行的异常。

Java中常用包:
在Java不同功能的类放在不同的包中,Java核心类主要放在Java包及其子包下,Java扩展的大部分类放在javax包及其子包下。
①java.lang:Java语言核心类,如String,Math,System,Thread等,此包中的类无需import导入,系统自动导入。
②java.util:Java的工具类、集合类等,如Arrays、List、Set等。
③java.net:Java网络编程相关类以及接口。
④java.io:Java输入输出类以及接口。
⑤java.awt:包含构建图形界面GUI相关类以及接口。
⑥java.sql:数据库编程常用的类以及接口。
⑦javax.swing:JGUI相关类以及接口。
⑧...

访问控制级别:
①private:此成员只能被该类的其他成员访问
②default:此成员只能被本包中其他类访问
③protected:此成员既能被同一包下其他类访问,也能被不同包下该类的子类访问
④public:此类成员能被所有类访问

Java中的多线程语法:
①通过继承Thread类来实现多线程
public class Test{
	new MyThread.start(); // 开启多线程
}
class MyThread extends Thread{
	public void run(){
		while(true){
			/*不断循环的线程*/
		}
	}
}
②通过实现Runnable接口来创建多线程
public class Test{
	new Thread(new MyRunnable).start(); // 开启多线程
}
class MyRunable implements Runnable{
	public void run(){
		while(true){
			/*不断循环的线程*/
		}	
	}
}
Runnable比Thread类来讲,可以避免由于Java单继承带来的局限性,可以相同代码的线程去处理统一资源的情况。

Java的线程概述:
t.setDaemon(true); // 将线程t设置为后台线程,此方法必须在start()方法之前调用。当进程中只有后台线程运行时,进程也就会结束了。线程生命周期从Thread对象创建完成开始,当run方法执行完毕或者线程中抛出一个未捕获的异常或者错误时线程生命周期便会结束。线程生命周期分为五态:
①New新建态:通过start()方法进入就绪态。新建一个线程所处的状态。
②Runnable就绪态:获得CPU使用权限变成运行态。调用start()方法所处的状态,位于可运行池中,只是具备了运行条件,运行还等待cpu调度。
③Running运行态:通过run()方法执行完或异常错误进入死亡状态。就绪态线程获得CPU使用权,开始执行run()方法实体
④Blocked阻塞态:失去CPU使用权限变成就绪态。后面具体讲解。
⑤Terminated死亡态:一旦进入死亡态,线程将不再拥有运行资格,也无法转到其他态。

线程阻塞状态:
常见于执行输入输出操作,进入阻塞,
①当线程获取某个对象的同步锁时,如果该锁被其他线程占有,当前线程阻塞。获得同步锁解除阻塞。
②当线程调用了一个阻塞的IO方法时,线程进入阻塞。等待IO方法返回解除阻塞。
③当线程调用某对象的wait()方法时,进入阻塞。用notify()方法唤醒线程进入就绪态。
④当线程调用了Thread的sleep()方法时,进入阻塞。等待线程睡眠时间到了,即可自动进入有序队列。
⑤当线程中调用了另一个线程的join()方法时,会使当前线程进入阻塞。等新加入的线程运行结束后才会进入就绪状态。

线程调度:
计算机中线程调度有两种模式,分为分时调度和抢占式调度,Java虚拟机默认是抢占式,当然也可以由程序自己来控制CPU的调度。
①设置线程优先级:通过thread.setPriority(10);设置thread这个线程优先级为10,数字越大优先级越高
②线程休眠:通过Thread.sleep(时间)静态方法使得人为控制当前线程阻塞一段时间,此方法要处理异常,通常直接捕获异常InterruptedException或者继续抛出异常给调用者处理。
③线程让步:通过Thread.yield()方法使得当前线程直接由运行态进入就绪态而不是阻塞态。
④线程插队:通过thread.join();使当前线程进入阻塞,直到被插入的线程执行完后才会进入就绪状态。

多线程同步:
同步代码块:可以保证处理共享资源的代码在任何时刻只能有一个线程访问
synchronized(lock){  // lock为Object类型
	/*操作共享资源的代码*/
}

lock为一个锁对象,默认情况锁对象的标志位为1,为1时可执行此此同步代码块,且将此锁对象的标志位置0;为0则线程发生阻塞。

同步方法:synchronized 返回值类型 方法名(参数...),同步方法同样也是有锁的,他的锁就是this对象。同步代码块和同步方法都是用来解决线程安全问题。

死锁问题:两个线程运行到一半,A线程要使用B的资源,此事B线程要使用A的资源,于是二者发生死锁。多线程通信的wait(),notify()来解决死锁问题。

多线程通信:
如在操作取操作和存操作两个线程同时处理一个空间,当存取满空间时,回到0下标,这是可能出现问题的,于是需要线程间通信。需要用到wait(),notify(),notifyAll()方法用来解决问题。

Java API:
①String,StringBuilder,StringBuffer:Sting字符串常量,StringBuilder非线程安全,StringBuffer线程安全;String是不可改变的对象,对此对String对象进行改变其实都是等同于生成一个新的String对象,然后将指针指向新的String对象,而StringBuffer会对对象本身操作,所以当字符串经常需要改变的情况下推荐使用StringBuffer类;StringBuilder是JDK5.0以后新增的,是StringBuffer的一个简易替换,但是不保证线程同步的安全性,常被用在字符串缓冲区被单个线程使用,这时候优先采用此类,此时他会比StringBuffer还要快,二者的方法基本相同。StringBuffer和StringBuilder默认容量为16,当容量存储超过16时他们会自动扩容,扩容机为2*n+2。
②System和Runtime:System常用的方法有终止当前虚拟机,有运行垃圾回收器,返回当前时间等他们都是静态方法;Runtime类用来专门表示虚拟机的运行状态,每次启动java.exe都对应一个Runtime实例,所以此类是采用单例模式进行设计的。
③Math与Random:Math类很常用提供了许多方便的数学方法,其中有两个静态常量,PI和E表示π和e;Random在java.util包中,可以在指定的取值范围内产生随机数,其有两个构造方法Random()和Random(long seed),第一个构造方法的种子是当前的时间戳,而第二个构造方法的种子是传进去的seed,有参数的构造方法的对象两次实验结果会相同,而无参的会不同,因为时间在改变。
public static void main(Sting args[]){ // 无参的举例
	Random r=new Random();
	for(int x=0;x<10;x++){
		System.out.println(r,nextInt(100)); // 0到100的整型随机数
	}
}

当然Random中还有其他的方法。

④Date,Calendar,DateFormat:三个类都是对日期进行操作,前二者在java.util包下,后面一个在java.text包下,在Date类中大部分构造方法都被声明为已过时,只有Date()和Date(long date)还建议使用,date参数表示自1970.01.01.00:00以来的毫秒数,即时间戳,从JDK1.1开始Calendar类取代了Date的大部分功能;与Date类不同,Calendar是一个abstract class,不能实例化对象,可以通过静态方法getInstance()来得到一个对象,他不是单例模式,Calendar有两种模式,一个是日历字段模式lenient(default),其字段可以超过允许值的范围,如指定13月也是不报错的,另一种是non-lenient模式,当超过限度时会报出异常;DateFormat可以将日期格式转换为字符串或者特定格式显示的日期字符串转化为一个Date对象,DateFormat还有个子类是SimpleDateFormat,他可以更加灵活。

包装类:
由于在Java设计中很多方法是要接受引用类型的数据对象,无法将基本数据类型传入,于是有了包装类的概念,包装类可以将基本数据类型的值包装成引用数据类型对象。
①boolean--Boolean
②byte--Byte
③char--Character
④short--Short
⑤int--Integer
⑥long--Long
⑦float--Float
⑧double--Double
装箱
int a;
Integer in=Integer(a);
拆箱
int b=in.intValue();
包装类都重写了Object类的toString()方法,还有许多常用的方法等。在JDK5.0以后提供了自动拆装箱。

集合类概述:
集合按照存储结构分两大类:
Collection单列结合(接口)
List有序可重复(接口)
ArrayList(类)(线程不安全)
LinkedList(类)
Vector(类)(线程安全)
Set无序不可重复(接口)
HashSet(类)
TreeSet(类)
Map双列集合(接口)
HashMap(类)(线程不安全)
TreeMap(类)
HashTable(类)(线程安全)
Properties(类)

集合类细讲:
①Collection接口:所有单列集合的父接口,里头有size()方法,iterator()方法等。
②List接口:单列接口,元素有序且可重复,线性方式存储。
③ArrayList集合:单列有序可重复基于线性表结构,线程不安全,是程序中最常见的集合,内部封装了一个长度可变的数组,add(Object o)用来存储元素,get(int index)用来取出下标为index的元素。
④LinkedList集合:单列有序可重复基于双向循环链表结构,同时有方便的插入删除操作方法。
⑤Iterator接口:他是一个迭代器,通常用法:
Iterator it=list.iterator();
while(it.hasNext()){
	Object obj=it.next();
}
⑥ListIterator接口:为Iterator的子类,与Iterator不同,ListIterator可以提供反向的迭代。
ListIterator it=list.listIterator(list.size()); // 括号内为从哪里开始
while(it.hasPrevious()){
	Object obj=it.previous();
}
⑦Enumeration接口:一个老式的迭代器,用法和Iterator类似
Vector v=new Vector();
Enumeration en=v.elements();
while(en.hasMoreElements()){
	Object obj=en.nextElement();
}
⑧Vector集合:用法与ArrayList完全相同,但他是线程安全的。之所以ArrayList类是线程不安全,因为他们作为线性表结构,存储数据有两步,第一:存储,第二:下标或指针往后移位,对于Vector类而言,程序开发者希望他是线程安全的,于是在处理存储操作方法用同步方法来代替。
⑨Set接口:单列接口,元素无序不可重复,非线性存储结构。
⑩HashSet集合:单列无序不可重复基于散列表的存储结构,不是同步的。向HashSet结合中存储元素时,首先会自动用该对象的hashCode()方法确定元素存储位置,之后调用对象equals()方法确保该位置没有重复。
①①TreeSet集合:单列有序不可重复基于自平衡的排序二叉树存储结构。
①②Map接口:所有双列结的父接口。Map集合的两种遍历方式:首先用Map对象的keySet()方法,获得Map中所有键的Set集合,然后用Iterator迭代Set集合的每一个元素,最后调用get(String key)方法根据键获取对应的值。或者先获取集合中的所有映射关系,然后从映射关系中取出键和值。如下:
Map map=new HashMap();
/*已向集合中加入了元素*/
Set keySet=map.keySet();
Iterator it=keySet.iterator();
while(it.hasNext()){
	Object key=it.next();
	Object value=map.get(key);
}
或者:
Map map=new HashMap();
/*已向集合中加入了元素*/
Set entrySet=map.entrySet();
Iterator it=entrySet.iterator();
while(it.hasNext()){
	Map.Entry entry=(Map.Entry)(it.next());
	Object key=entry.getKey();
	Object value=entry.getValue();
}
①③HashMap集合:双列无序键不可重复基于哈希值算法的存储结构,线程不安全。若想使迭代的元素顺序
和存入的是一致的,可以用HashMap的子类LinkedHashMap,内部是双向链表。
①④TreeMap集合:双列有序键不可重复基于红黑树的存储结构。在使用TreeMap时也可以自定义比较器对所有键进行排序:
public class Example{
	public static void main(String argsp[]){
		TreeMap tm=new TreeMap(new MyComparator());
		Set keySet=tm.keySet();
		Iterator it=keySet.iterator();
		while(it.hasNext()){
			Object key=it.next();
			Object value=tm.get(key);
		}
	}
}
class MyComparator implements Comparator{ // 自定义的比较器
	public int compare(Object obj1,Object obj2){
		String id1=(String) obj1;
		String id2=(String) obj2;
		return id2.compareTo(id1);
	}
}
①⑤HashTable集合:双列无序键不可重复基于哈希值算法的存储结构,线程安全。用法和HashMap一样。
①⑥Properties集合:是HashTable的子类,实际应用中很重要,用来存储字符串类型的键和值,存取应用的配置项。setProperty()和getProperty()是对字符串存取的两个重要方法。

泛型:
泛型是JDK5之后采用的,用泛型因为集合存储元素可以存任何类型对象,但存入后,将对象取出后,对象就变成类Object类型,如:ArrayList<String>list=new ArrayList<String>();这样可以在编译时就提前发现问题不会等到运行时在发现问题。自定义泛型如下:
class cachePool<T>{
	T temp;
	public void save(T temp){
		this.temp=temp;
	}
	public T get(){
		return temp;
	}
}
public class Example{
	public static void main(String args[]){
		cachePool<Integer>pool=new cachePool<Integer>();
		pool.save(new Integer(1));
		Integer temp=pool.get();
	}
}


集合类中的工具类:
①Collections工具类:Collections工具类中有许多静态方法方便对集合中元素进行排序查找修改等操作。
②Arrays工具类:Arrays工具类中有许多静态方法方便对数组重元素进行操作。有诸如sort方法binarySearch方法copyOfRange方法fill方法等常用方法。

IO输入输出概述:
IO流:
字节流:
InputStream字节输入流(抽象类)
FileInputStream(类)读取文件数据
BufferedInputStream(包装类)带缓冲流的字节流
OutputStream字节输出流(抽象类)
FileOutputStream(类)文件
BufferedOutputStream(包装类)带缓冲流的字节流
字符流:
Reader字符输入流(抽象类)
BufferedReader(包装类)
LineNumberReader(包装类)
InputStreamReader(包装类)
FileReader文件字符输入流
Writer字符输出流(抽象类)
BufferedWriter(包装类)
OutputStreamWriter(包装类)
FileWriter文件字符输出流

IO输入输出细讲:
①InputStream:为抽象类,是字节输入流的定级父类,有read方法close方法等常用方法,其下有许多子类。
②OutputStream:为抽象类,是字节输出流的定级父类,有write方法flush方法close方法等常用方法,其下有许多子类。
③FileInputStream:是InputStream的子类,文件字节输入流,
FileInputStream in=new FileInputStream("test.txt"); // 从文件读入为文件输入流
int b=0;
while(true){
	b=in.read(); // 读入的数据保存到b(int)
	if(b!=-1)
		break; // 判断是否读完
}
in.close(); // 关闭流
④FileOutputStream:是OutputStream的子类,文件字节输出流,
FileOutputStream out=new FileOutputStream("example.txt");
String str="nihao";
byte[] b=str.getBytes(); // 将String转化为byte[]数组
for(int i=0;i<b.length;i++){
	out.write(b[i]);
}
out.close(); // 关闭流
⑤BufferedInputStream:是FileInputStream的子类,是一个包装类,采用装饰设计模式。
⑥BufferedOutputStream:是FileOutputStream的子类,是一个包装类,采用装饰设计模式。如:
BufferedInputSream bis=new BufferedInputStream(new FileInputStream("src.txt"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("des.txt"));
int len;
while((len=bis.read())!=-1){
	bos.write(len);
}
bis.close();
bos.close();
⑦Reader:为抽象类,是字符输入流的顶级父类,有read方法close方法等常用方法,其下有许多子类。
⑧Writer:为抽象类,是字符输出流的顶级父类,有write方法close方法等常用方法,其下有许多子类。
⑨FileReader:Reader→InputStreamReader→FileReader,文件字符输入流。
⑩FileWriter:Writer→OutputStreamWriter→FileWriter,文件字符输出流。FileWriter和FileOutputStream类一样,如果指定文件不存在,就会先创建文件,再写入数据,如果文件存在,就会先清空文件内容再写入数据。
①①BufferedReader:Reader→BufferedReader,是一个包装类,可以包装FileReader类型。
①②BufferedWriter:Writer→BufferedWriter,是一个包装类,可以包装FileWriter类型。
①③LineNumberReader:Reader→BufferedReader→LineNumberReader,是一个包装类,可以包装FileReader类型。是一个可以跟踪行号的输入流。
①④InputStreamReader:包装流,可以将字节输出流包装成字符输入流。
①⑤OutputStreamWriter:包装流,可以将字节输出流包装成字符输出流。
①⑥其他IO流:

装饰设计模式:
通过包装一个类,动态的为他增加功能的一种设计模式。

Java GUI:
GUI是用户图形界面,Java GUI的丰富类库在java.awt和javax.swing包中。
①AWT:提供了窗口,按钮,文本框,对话框等。布局管理器有五种:流式布局管理器FlowLayout,边界布局管理器BorderLayout,网格布局管理器GridLayout,网格包布局管理器GridBagLayout,卡片布局管理器CardLayout。在java.awt包中专门提供了一个Graphics类,类似于一个画笔,其中提供了各种绘制图形的方法。
②Swing:相对于AWT而言,Swing包中提供更加丰富的GUI组件,由于有Java编写,AWT叫做重量级组件,而不依赖于本地平台的Swing组件叫轻量级组件。

Java网络编程:
①TCP/IP协议四层结构:应用层(HTTP,FTP,DNS)→传输层(TCP,UDP)→网络层(IP,ICMP,IGMP)→链路层(驱动程序,接口)
②JDK中提供了一个InetAddress类,用于封装一个IP地址,并提供了一些列和IP地址相关的方法,此类在java.net包中。
③UDP协议:无连接的通信协议,发送端不会确认接收端的存在而去发送,接收端也不会向发送端反馈。UDP协议消耗资源小,通信效率高,常用于音频视频和普通数据传输。
④TCP协议:面向连接的通信协议,在TCP连接中要明确客户端和服务端,客户端向服务端发送连接请求,每次连接的创建都需要经过“三次握手”,第一次握手,客户向服务器发送连接请求,等待服务器确认;第二次握手,服务器向客户端回送一个响应,通知客户端收到连接请求;第三次握手,客户端再次向服务端发送确认信息,确定下载文件时必须采用TCP协议连接。
⑤TCP的三次握手:第一次握手:建立连接时,客户端发送syn包,(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。完成三次握手,客户端与服务器开始传送数据。
⑥TCP报文:需要注意的是TCP协议的报文格式是有"Seq序号"占32位,对发送数据一方进行标记;有"确认序号Ack",占32位,只有ACK=1时,确认序号字段才有效,Ack=1+Seq;标志位共有6个:URG,ACK,PSH,PST,SYN,FIN等,其中ACK确认序号有效字段,SYN发起一个新连接,FIN释放一个连接。注意一定不要把Ack确认序号和标志位中ACK字段搞混了。

Java UDP/TCP通信:
在JDK中提供了InetAdress类,用于封装一个IP地址,并提供了与IP相关的一系列方法。
①UDP通信:是无连接的通信协议。消耗资源小,通信效率高,可能丢包。DatagramPacket类用于封装UDP通信中发送的数据包,其中提供了许多方法用于获取数据相关的信息,如InetAddress getAddress(),int getPort(),byte[] getData(),int getLength(),DatagramSocket类用来接收和发送DatagramSocket数据包。
/*receive端*/
import java.net.*;
public class Example{
	public static void main(String args[]) throws Exception {
		DatagramSocket ds=new DatagramSocket(8954); // 创建接收端的Socket,监听端口为8954

		byte[] buf=new byte[1024];
		DatagramPacket dp=new DatagramPacket(buf,1024); // 创建一个数据包集装箱
		
		ds.receive(dp); // 等待接受数据,若没有数据则会阻塞

		ds.close(); // 关闭套接字
	}
}
/*send端*/
import java.net.*
public class Example2{
	public static void main(String args[]){
		DatagramSocket ds=new DatagramSocket(3000);创建发送端的Socket,监听的端口为3000
		
		String str="hello world";
                // 创建一个要发送的数据包集装箱,有要发的数据,数据长度,接收端IP,目的端口号
                DatagramPacket dp=new DatagramPacket(str.getBytes(),str.length(),InetAddress.getByName("localhost"),8954);
                ds.send(dp); // 发送数据
                ds.close(); // 关闭套接字
       }
}

②TCP通信:是面向连接的通信协议,其中存在"三次握手",JDK中提供了两个类用于实现TCP通信,一个是ServerSocket表示服务端,另一个是Socket表示客户端。ServerSocket类中有Socket accept(),InetAddress getInetAddress(),boolean isClosed(),void bind(SocketAddress endpoint);Socket类中有int getPort(),InetAddress getLocalAddress(),void close(),InputStream getInputStream(),OutputStream getOutputStream()。

/*server端*/
import java.io.*;
import java.net.*;
public class Example3{
	public static void main(String args[]) throws Exception {
        /* 创建服务端Socket,并监听7788端口。注意,若这里没有指明端口号,意味着没有监听端口,则后面还需要用bind(SocketAddress endpoint)方法监听绑定到指定的端口号,才可以正常使用 */
		ServerSocket serverSocket=new ServerSocket(7788); 
		
		Socket client=serverSocket.accept(); // 调用accept方法接收数据,连接之前是阻塞态

		OutputStream os=client.getOutputStream(); // Socket的输出流
		os.write( ("hello world").getBytes() );
		
		os.close();
		client.close(); // 关闭套接字
	}
}
/*client端*/
import java.io.*;
import java.net.*;
public class Example4{
	public static void main(String args[]) throws Exception {
        /* 指定目标IP和目标端口号。注意,若这里没有指定目标IP和目标端口号,意味着没有去连接任何的server,则后面还需要用connect(SocketAddress endpoint)方法,才能完成与指定服务器端的连接 */
		Socket client=new Socket(InetAddress.getLocalHost(),7788);
		
		InputStream is=clinet.getInputStream(); // 接收数据的流
		byte[] buf=new byte[1024]; // 缓冲数组
		int len=is.read(buf); // 将数据读到缓冲中

		client.close();
	}
} 
 

猜你喜欢

转载自blog.csdn.net/abcnull/article/details/79548120