首页 > 编程语言 > 详细

异常高级、Socket和线程基础

时间:2021-03-15 00:10:22      阅读:23      评论:0      收藏:0      [点我收藏+]

异常高级,Socket和线程基础

异常高级

1 throws关键字

throws关键字表示一个方法中可能发生的异常类型

一个方法标明方法中可能抛出的异常类型后,调用它的方法可以处理这些可能发生的异常

public void dosome() throws IOException,AWTException{
}

其中dosome是方法名,IOException,AWTException是可能发生的异常类型

2 异常的分类

check异常:SQLException、IOException......

运行时异常:ClassCastException、ArrayIndexOutOfBoundsException、NullPointerException、RuntimeException......

3 子类重写父类方法时throws规则
package cn.tedu.vip.exception;

import java.awt.AWTException;
import java.awt.FontFormatException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
/**
 * 子类重写父类含有异常声明的方法时子类方法的重写规则
 * @author Tedu
 *
 */
public class ThrowOverrideDemo {

	public void dosome() throws IOException,AWTException{
		
	}
}
class Sub extends ThrowOverrideDemo{
	//一模一样
//	public void dosome() throws IOException,AWTException{
		
	//}
	//抛出其中一个
//	public void dosome() throws IOException{
		
	//}
	//一个异常都不输出 OK!!!
//	public void dosome() {
		
	//}
	//抛出父类声明异常的子类
//	public void dosome() throws FileNotFoundException{
		
//	}
	//抛出父类没有声明的其他异常 No!!!
//	public void dosome() throws SQLException{
		
	//}
//	
    //抛出父类声明异常类型的父类  NO!!!
//    public void dosome() throws Exception{
    	
//    }
    
}
4 自定义异常类

自定义异常需要注意如下几点:

  1. 定义好类名,做到见名知意
  2. 需要继承Exception(直接或间接都可以)
  3. 定义序列化版本号(避免出现警告)
  4. 重写Exception定义的所有构造方法
package cn.tedu.vip.exception;
/**
 * 年龄不合法的异常类
 * 
 * 自定义异常,通常用来说明我们程序中的业务逻辑问题
 * 注意事项:
 *1:类名见名知意
 *2:继承Exception(直接或间接均可)
 *3:要定义序列化版本号
 *4:重写父类的构造方法
 * @author Tedu
 */
public class IllegalAgeException extends Exception{
	
	private static final long serialVersionUID = 1L;
	
	public IllegalAgeException() {//alt+/ 无参数构造
		super();	
	}

	public IllegalAgeException(String message) {
		super(message);
	}

	public IllegalAgeException(Throwable cause) {
		super(cause);
	}

	public IllegalAgeException(String message, Throwable cause) {
		super(message, cause);
	}

	public IllegalAgeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}
}
5 使用自定义异常

演示如何抛出刚刚定义的自定义异常对象:

package cn.tedu.vip.exception;
/**
 * 模拟一个实体类,当前业务中只考虑age属性,和异常的情况
 * @author Tedu
 *
 */
/**
 * @author hp
 *
 */
public class Person {
	private String name;
	private int age;
	//....
	public int getAge() {
		return age;
	}
	public void setAge(int age) throws IllegalAgeException {
		if(age<0 || age>120) {
			/*
			 * 根据业务需求,可以在age值不符合要求时
			 * 主动抛出异常,表示程序中这样的赋值不被允许
			 * throw 关键字 后面实例化一个异常对象,当前代码会发生这个类型的异常
			 * 当前方法会在运行throw后终止,功能类似return
			 */
			throw new IllegalAgeException("年龄不合法");
		}
		this.age = age;
	}
}

测试类中去处理年龄赋值发生的异常:

package cn.tedu.vip.exception;
/**
 * 使用person类演示自定义异常类的使用,抛出,处理
 * @author hp
 *
 */
public class ThrowDemo {

	public static void main(String[] args) {
		Person p=new Person();
		
		try {
			p.setAge(50);
		} catch (IllegalAgeException e) {
			e.printStackTrace();
		}
		
		System.out.println("年龄是:"+p.getAge());
	}
}

Socket网络编程

1 C/S与B/S架构

C/S是Client/Server,既客户端\服务器

B/S是Browser/Server,既浏览器\服务器,浏览器本质是一个标准化的客户端

2 TCP与UDP

TCP:可靠传输协议

? 传输的信息必须保证送达目标计算机,但是速度慢,占资源多

UDP:不可靠传输协议:

? 传输的信息不保证送达目标计算机,但是速度快,即时性强,占用资源少

3 Java网络编程

Socket类:

封装了TCP协议的通讯细节,使用它就可以完成与远端计算机的TCP连接以及数据传输,完成数据传输就是基于两条流的读写进行。

4 在线聊天室

客户端:

package socket;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.Scanner;
/**
 * 聊天室客户端
 * 
 * @author Tedu
 *
 */
public class Client {
	/*
	 * java.net.Socket Socket封装了TCP通信细节,我们只需要明确IP地址和端口号 向服务器发送输出流输入流即可
	 */
	private static Socket socket;

	public Client() {
		try {
			/*
			 * 实例化Socket需要两个参数 1:服务器端IP地址 2:服务器端,端口号
			 * 
			 * 只要实例化成功就是连接成功 如果连接失败就会出现异常
			 * 
			 * 端口号是计算机提供服务的接口的编码 Ip地址是计算机在网络中的所在位置
			 */

			System.out.println("开始连接");
			socket = new Socket("localhost", 8088);
			System.out.println("连接成功");
		} catch (Exception e) {
			e.printStackTrace();
		}
		/*
		 * 开始搜索cmd 然后在弹出的界面中输入ipconfig观察本机的ip地址
		 */
	}

	public void start() {
		/*
		 * 要想发出信息,需要从socket对象中获得输出流 
		 * OutputStream getOutputStream();
		 */
		try {
			//启动接收服务器消息的线程
			ServerHandler handler=new ServerHandler();
			Thread t=new Thread(handler);
			t.start();



			OutputStream os = socket.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(os, "utf-8");// 把os转换成字符流
			BufferedWriter bw = new BufferedWriter(osw);// 转换成缓冲流
			PrintWriter pw = new PrintWriter(bw, true);// 自动调用

			Scanner scan = new Scanner(System.in);
			while (true) {
				String msg = scan.nextLine();
				pw.println(msg);// 输出一行
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {
		Client client = new Client();
		client.start();
	}
	/*
	 * 编写一个内部类
	 * 用于创建接收服务器端信息的线程
	 */
	class ServerHandler implements Runnable{
		public void run() {
			try {
				InputStream is=(InputStream) socket.getInputStream();
				InputStreamReader isr=new InputStreamReader(is,"utf-8");
				BufferedReader br=new BufferedReader(isr);

				String msg=null;
				while((msg=br.readLine())!=null) {
					System.out.println(msg);
				}
			} catch (Exception e) {

			}
		}
	}	
}

服务器端代码:

package socket;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.concurrent.BrokenBarrierException;
/**
 * 聊天室服务器端
 * 
 * @author Tedu
 *
 */
public class Server {
	/*
	 * java.net.ServerSocket 服务器端运行这个类的对象 1:能申请服务端口 2:能够监听客服端发送过来的信息
	 */
	private ServerSocket server;
	/*
	 * 定义一个输出流数组,保存的是指向各个客户端的输出流
	 */
	private PrintWriter[] allout= {};

	public Server() {
		try {
			/*
			 * 实例化ServerSocket对象 向操作系统申请端口 如果这个端口被占用会发生异常
			 */
			System.out.println("服务器正在启动");
			server = new ServerSocket(8088);
			System.out.println("服务器启动完毕");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void start() {
		/*
		 * Socket accept() 该方法是一个阻塞方法,调用后,程序暂停等待客服端的信息发送过来 没有信息过来就一直等
		 */
		try {
			while(true) {
				System.out.println("等待客户端连接");
				Socket socket = server.accept();
				System.out.println("一个客户端连接了");
				/*
				 * 循环等待客户端连接
				 * 每个客户端连接会得到这个客户端的socket
				 * 获得之后开启新的线程,将socket对象赋给新线程
				 * 启动线程
				 */
				ClientHandler handler=new ClientHandler(socket);
				Thread t=new Thread(handler);
				t.start();

				/*
				 * 从socket中获得输入流 InputStream getInputStream()
				 */
				//InputStream is = socket.getInputStream();
				//InputStreamReader isr = new InputStreamReader(is, "utf-8");
				//BufferedReader br = new BufferedReader(isr);

				/*
				 * 这里需要使用下面的方式来读取客户端发送过来的内容
				 * br.readLine()这个方法在不同系统下运行时会有不同的反应
				 * windows系统,客户端断开会发生异常
				 * linux系统客户端的断开返回空,当返回null时保证循环能够结束
				 */
				//String msg;
				//while((msg=br.readLine())!=null) {
				//	System.out.println(msg);
				//}

				//while(true) {
				//String msg = br.readLine();
				//System.out.println(msg);
				//}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {
		Server server = new Server();
		server.start();
	}
	/*
	 * 创建一个内部类,专门处理客户端请求
	 */
	class ClientHandler implements Runnable{
		//客户端发送过来的socket对象
		private Socket socket;

		//客户端的地址信息
		private String host;

		public ClientHandler(Socket socket) {
			this.socket=socket;
			this.host=socket.getInetAddress().getHostAddress();
		}
		public void run() {
			PrintWriter pw=null;
			/*
			 * 获得客户端发送过来数据的输入流
			 */
			try {
				InputStream is=socket.getInputStream();
				InputStreamReader isr=new InputStreamReader(is,"utf-8");
				BufferedReader br=new BufferedReader(isr);
				/*
				 * 获得发送到客户端信息的输出流
				 */
				OutputStream os=socket.getOutputStream();
				OutputStreamWriter osw=new OutputStreamWriter(os,"utf-8");
				BufferedWriter bw=new BufferedWriter(osw);
				pw=new PrintWriter(bw,true);

				synchronized (allout) {
					//这个代码是有新客户端连接时运行一次的
					//目的是将这个pw放入输出流数组中
					//1:数组扩容
					allout=Arrays.copyOf(allout, allout.length+1);
					//2:将当前获得的输出流放入数组中
					allout[allout.length-1]=pw;
				}
				System.out.println(host+"上线了,当前在线人数:"+allout.length);


				/*
				 * 使用BUfferedReader的ReadLine方法获得客户端发送过来的数据
				 * 并循环获取,直到客户端终止运行
				 * windows系统会发生异常,linux会返回null
				 */
				String msg=null;
				while((msg=br.readLine())!=null) {
					System.out.println(host+"说:"+msg);
					//便利输出流数组,向所有客户端输出信息
					synchronized (allout){
						for (int i = 0; i < allout.length; i++) {
							allout[i].println(host+"说:"+msg);
						}
					}					
				}
			} catch (Exception e) {
			}finally {
				//客户端断开后的操作
				//将当前客户端的输出流从数组中移除
				synchronized (allout) {
					for (int i = 0; i < allout.length; i++) {
						if(allout[i]==pw) {
							allout[i]=allout[allout.length-1];
							allout=Arrays.copyOf(allout, allout.length-1);//缩容
							break;
						}
					}
				}
				System.out.println(host+"下线了,当前在线人数:"+allout.length);
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}	
}

线程基础

1 什么是线程

一条线程就是一组任务执行序列

2 什么是多线程

多线程就是在某一时间段内并发多个任务执行序列

3 为什么需要多线程

有了多线程可以同时运行多个任务,有效利用资源,让使用者有更好的体验。

4 创建线程的两种方式

方式1:

package thread;
/**
 * 创建线程的方式一: 编写一个继承java.lang.Thread类的子类
 * 
 * 多线程本质上改变了代码的执行方法
 * 由一句一句执行,改变成了多线程并发执行
 * 并发执行不是真正意义上的同时执行,只是利用CPU的速度模拟了这样的现象
 * 
 * @author Tedu
 *
 */
public class ThreadDemo1 {

	public static void main(String[] args) {
		/*
		 * 要开启线程,必须实例化Thread类的对象
		 */
		MyThread1 t1 = new MyThread1();
		MyThread2 t2 = new MyThread2();
		/*
		 * 使用Thread类对象的start方法开启新线程 我们重写的是run方法,但是不能调用它
		 */
		t1.start();
		t2.start();
	}
}

class MyThread1 extends Thread {
	/*
	 * 要想开启一个新的线程,必须在继承Thread类之后 重写父类中的run()方法
	 * 这种方式的优点:代码简单,适合编写匿名内部类的形式
	 * 缺点:
	 * 1:java代码单继承,一旦这个类继承了Thred就不能继承其他类了
	 * 2:将这个线程能做什么直接固定了,不能修改,增加了程序的耦合性,不利于线程重用
	 */
	public void run() {
		/*
		 * run方法中的代码就是开启新线程要执行的任务序列
		 */
		for (int i = 0; i < 100; i++) {
			System.out.println("你是谁");
		}
	}

}

class MyThread2 extends Thread {
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println("我是小可爱");
		}
	}
}

方式二:

package thread;
/**
 * 创建线程的方式二
 * 
 * @author Tedu
 *
 */
public class ThreadDemo2 {

	public static void main(String[] args) {
		// 实例化两个任务序列
		MyRunnable1 r1 = new MyRunnable1();
		MyRunnable2 r2 = new MyRunnable2();

		// 创建两条线程,并指定任务
		Thread t1 = new Thread(r1);
		Thread t2 = new Thread(r2);
		// 运行
		t1.start();
		t2.start();
	}
}

/*
 * 方式二,实现java.lang.Runnable接口 这个接口的实现类可以赋值给Thread对象当做线程执行的任务序列用
 * 
 * 优点:任务和线程对象分离。耦合性低
 * 缺点:1:需要额外的代码实例化任务对象,代码比较多
 * 2:不继承Thread类,有些方法没有办法直接使用
 */
class MyRunnable1 implements Runnable {
	/*
	 * 
	 * 必须重写接口中声明的run方法
	 */
	@Override
	public void run() {
		for (int i = 0; i < 1000; i++) {
			System.out.println(i+":就不爱听你们叽叽喳喳");
		}

	}
}

class MyRunnable2 implements Runnable {
	public void run() {
		for (int i = 0; i < 1000; i++) {
			System.out.println(i+":烦人");
		}

	}
}

综合两种方式使用匿名内部类的方式创建:

package thread;
/**
 * 使用匿名内部类创建线程
 * @author Tedu
 *
 */
public class ThreadDemo3 {

	public static void main(String[] args) {
		//第一种方式
		Thread t1=new Thread() {
			public void run() {
				for (int i = 0; i < 1000; i++) {
					System.out.println(i+":你是逗逗吗?");
				}
			}
		};
		//第二种方法
		Runnable r=new Runnable() {
			public void run() {
				for (int i = 0; i < 1000; i++) {
					System.out.println(i+":你才是逗逗呢!");
				}
			}
		};
		Thread t2=new Thread(r);
		
		t1.start();
		t2.start();
	}
}
5 获得当前线程

static Thread currentThread()

获得当前线程的对象,可以从对象中获得一些当前线程的信息

package thread;
/**
 * static Thread currentThread() 这个方法直接返回正在运行的线程的对象
 * 
 * @author hp
 *
 */
public class CurrentThreadDemo {

	public static void main(String[] args) {
		/*
		 * main方法实际上也是一条线程,只是不由程序员创建
		 */

		// Thread m=Thread.currentThread();
		// System.out.println(m);
		Thread t = new Thread() {
			public void run() {
				Thread t = Thread.currentThread();
				System.out.println(t);
				dosome();
			}
		};
		t.setName("tom");
		t.start();
	}

	public static void dosome() {
		Thread t = Thread.currentThread();
		System.out.println(t);
	}
}
6 线程信息

常见的线程信息API

package thread;
/**
 * 了解线程信息相关API
 * @author Tedu
 *
 */
public class ThreadInfoDemo {

	public static void main(String[] args) {
		//获取线程对象
		Thread t=Thread.currentThread();
		
		//线程名称
		String name=t.getName();
		System.out.println("线程名称:"+name);
		
		//线程标识
		long id=t.getId();
		System.out.println("线程唯一标识:"+id);
		
		//是否正常运行
		boolean isAlive=t.isAlive();
		System.out.println("是否活着:"+isAlive);
		
		//守护线程
		boolean isDaemon=t.isDaemon();
		System.out.println("是否是守护线程:"+isDaemon);
		
		//是否中断
		boolean isInterrupted=t.isInterrupted();
		System.out.println("是否中断:"+isInterrupted);
		
		//优先级
		int priority=t.getPriority();
		System.out.println("优先级:"+priority);
	}
}

异常高级、Socket和线程基础

原文:https://www.cnblogs.com/xiongchenglong/p/14534418.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!