throws关键字表示一个方法中可能发生的异常类型
一个方法标明方法中可能抛出的异常类型后,调用它的方法可以处理这些可能发生的异常
public void dosome() throws IOException,AWTException{
}
其中dosome是方法名,IOException,AWTException是可能发生的异常类型
check异常:SQLException、IOException......
运行时异常:ClassCastException、ArrayIndexOutOfBoundsException、NullPointerException、RuntimeException......
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{
// }
}
自定义异常需要注意如下几点:
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);
}
}
演示如何抛出刚刚定义的自定义异常对象:
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());
}
}
C/S是Client/Server,既客户端\服务器
B/S是Browser/Server,既浏览器\服务器,浏览器本质是一个标准化的客户端
TCP:可靠传输协议
? 传输的信息必须保证送达目标计算机,但是速度慢,占资源多
UDP:不可靠传输协议:
? 传输的信息不保证送达目标计算机,但是速度快,即时性强,占用资源少
Socket类:
封装了TCP协议的通讯细节,使用它就可以完成与远端计算机的TCP连接以及数据传输,完成数据传输就是基于两条流的读写进行。
客户端:
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:
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();
}
}
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);
}
}
常见的线程信息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);
}
}
原文:https://www.cnblogs.com/xiongchenglong/p/14534418.html