多线程 & 反射 & 注解 & 网络编程 & 路径
一、多线程
1、概述
1.1 理解线程和进程
• 进程时一个应用程序(软件)
• 线程是一个进程的执行过程/执行场景
• 一个进程可以启动多个线程
1.2 java中实现多线程的两种方式
1. 创建一个类,直接继承java.lang.Thread 重写run()方法
• 在run方法中的代码运行在分支线程中
2. 创建一个类,实现Runnable接口(常用)
方法
• start () //启动线程
• setName() //修改线程名字
• getName() //得到线程名字
• 默认线程名 Thread-0 /1/2
Thread.currentThread() 静态方法
//得到当前线程在哪里出现获取得就是拿的线程
Thread t1 = Thread.currentThread();
Thread.sleep(毫秒) 静态方法
//让线程睡眠 出现在哪里就是让那个线程睡眠
interrupt()
//唤醒睡眠的线程
原理就是直接进去catch语句
合理结束线程
s打一个布尔标记
实现线程的第三种方式 实现Callable接口
这种方式可以获取线程的返回值
1.3 代码-java实现多线程方式1 继承 Thread类
public class TreadTest001 {
public static void main(String[] args) {
A a = new A();
a.start(); // 启动线程
for (int i = 0; i < 1000; i++){
System.out.println("主线程"+i);
}
}
}
class A extends Thread{
@Override
public void run() {
for (int i = 0; i < 1000; i++){
System.out.println("分支线程"+i);
}
}
}
1.4 代码—java实现多线程方式2 实现 Runnable 接口
public class ThreadTest01 {
public static void main(String[] args) {
Thread s = new Thread(new MyThread02());
s.start(); //启动线程
for (int i = 0; i < 100; i++){
System.out.println("主线程"+i);
}
}
}
class MyThread02 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++){
System.out.println("分支线程"+i);
}
}
}
1.5线程的五种状态
二、多线程并发/守护线程
1、满足条件
1. 多线程并发
2. 有共享数据
3. 共享数据有修改的行为
2、解决线程安全问题
1. 线程排队执行(不能并发)
2. 尽量使用局部变量代替实例变量/静态变量
3. 如果必须是实例变量,那么可以考虑多new几个对象
4. 以上都不行采用synchronized
这种机制被称为”线程同步机制“
3、synchronized
线程同步机制语法
synchronized(共享对象) {
//同步代码块 (和谁同步,填谁和谁的同步对象)
}
synchronized 出现在实例方法上,锁的是this(不灵活)
public synchronized void doSome(){}
在静态方法上使用synchronized
表示找类锁,类锁永远只有一把
对象锁:一个对象一把锁,一百个对象一百把锁
类锁:100个对象,1把锁
sybchronized 在开发中尽量不要嵌套使用,容易死锁
局部变量不存在线程安全问题
4、守护线程
后台线程 例如:垃圾回收线程
特点:死循环,用户线程结束守护线程自动结束
语法
setDaemon(true); // 变成守护线程
t.setDaemon(true); //t线程变成守护线程
即使t线程里面是死循环,当主线程结束,守护线程也会结束
三、生产者和消费者
1、概述
• wait()和notify()
• 不是线程对象的方法
• 任何一个java对象都有的方法(Object自带)
• wait()和notify()必须建立在synchroniezd基础上
2、wait()
• Object o = new Object();
• o.wait();
• 表示让正在o对象上活动的线程进入等待状态(无期限)
• 直到调用o.notify()方法
• 会释放锁
3、notify()
• o.notify();
• 唤醒正在等待的线程
• 不会释放锁
4、死锁
//死锁
public class DeadThread {
public static void main(String[] args) {
Object a = new Object();
Object b = new Object();
MyThread1 one = new MyThread1(a,b); //线程对象one
MyThread2 two = new MyThread2(a,b); //线程对象two
one.start(); //启动线程one
two.start(); //启动线程two
}
}
class MyThread1 extends Thread{
Object one;
Object two;
public MyThread1(Object one, Object two) {
this.one = one;
this.two = two;
}
@Override
public void run() {
synchronized(one){
try {
Thread.sleep(2000); //睡眠2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (two){
}
}
}
}
class MyThread2 extends Thread{
Object one;
Object two;
public MyThread2(Object one, Object two) {
this.one = one;
this.two = two;
}
@Override
public void run() {
synchronized(two){
try {
Thread.sleep(2000); //睡眠两秒
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (one){
}
}
}
}
5、synchronized有三种写法:
第一种:同步代码块 灵活
synchronized(线程共享对象){
同步代码块;
}
第二种:在实例方法上使用synchronized
表示共享对象一定是this
并且同步代码块是整个方法体
第三种:在静态方法上使用synchronized
表示找类锁。
类锁永远只有1把。
就算创建了100个对象,那类锁也只有一把
对象锁:1个对象1把锁,100个对象100把锁。
类锁:100个对象,也可能只是1把类锁。
反射机制
概述
• 通过java语言中的反射机制,可以操作字节码文件 .class。
• 反射机制的相关类在 java.lang.reflect.* 包下
• 反射机制相关的重要的类
• java.lang.Class 整个字节码文件,代表一个类型
• java.lang.reflect.Method 字节码中的方法
• java.lang.reflect.Constructor 字节码中的构造方法
• java.lang.reflect.Field 字节码中的属性
获取字节码文件
要操作一个类的字节码文件,需要首先获取到这个类的字节码文件。
三种获取class文件方式
1. 通过java.lang.Class类中的forName() 来获取
Class c1 = Class.forName("com.yixuexi.test01.Test");
注意
forName()是静态方法
方法的参数是一个字符串 必须是一个完整的类名【带包名】
返回一个Class类型对象
2. java中任何一个对象,都有一个getClass()方法
Test test = new Test();
Class c2 = test.getClass();
3. java中任何一种类型,都有.class属性
Class c3 = Test.class;
c3代表 Test类型
注意:c3 == c1/c2 true 变量的内存地址相同
获取到Class后做的事情
通过newInstance()来实例化对象
Class c1 = Class.forName(“com.yixuexi.bean.User”);
Object obj = c1.newInstance();
底层会调用User的无参构造方法
如果写了有参构造,则默认的无参消失 报异常:java.lang.InstantiationException 实例化对象异常
Class.forName() 方法的执行
Class.forName(“com.yixuexi.Test”);
forName()方法 会导致类加载,类加载时会执行静态代码块
可变长度参数
语法:
public static void m(int... args){
}
类型后面跟3个点 …
• 可边长参数必须在形参列表中最后一个
• 可以当作一个数组来对待, 直接用for遍历args也可以
作用
调用时,传参可以 传 0-n个
通过反射机制得到Field(属性)了解
getFields()
• Class c = Class.forName(“com.yixuexi.bean.Student”);
• //获取类中所有的field,返回一个Field数组
• Field[] arr = c.getFields();
• // 返回对应下标的属性的名字
• String fieldName = arr[0].getName();
• 获取类中所有(public ) 属性
getDeclaredFields()
• //获取全部的属性,不管是不是私有的 公开的
• Field[] f = c.getDeclaredFields();
f[0].getType()
获取对应属性的类型
• Class s = f[0].getType(); //返回一个Class
• System.out.println(s.getName()); // 通过getName得到名字【带包名】
• System.out.println(s.getSimpleName()); //得到简单的类名
f[0].getModifiers()
• 返回对应属性的修饰符编号,每个数字是修饰符的代号
• 将代号数字转换成字符串: Modifier.toString(f[0].getModifiers()) 静态方法
getName() 得到这个属性的名字
通过反射机制访问对象属性(掌握)
设置(公开的)
//通过反射机制访问java对象的属性
Class c = Class.forName("com.yixuexi.bean.Student");
//通过反射机制创建一个对象
Object obj = c.newInstance();
//获取id属性,根据属性的名称获取Field
Field f = c.getDeclaredField("id");
//赋值 三要素:obj对象, id属性 1111值
f.set(obj,1111);// 给obj的id属性赋值1111
访问(公开的)
System.out.println(f.get(obj)); //访问obj的id属性
访问私有属性需打破封装
//打破封装
f.setAccessible(true);
在set或者get上面 添加这行代码
※反射机制调用方法
反射机制调用方法
//通过反射机制调用方法 获取类
Class c = Class.forName("com.yixuexi.bean.Student");
//创建对象
Object obj = c.newInstance();
//获取Method, 第一个参数是方法名,第二个是可变长度类型Class
Method doSomeMethod = c.getDeclaredMethod("doSome", String.class, int.class);
//调用方法 调用obj对象的 doSomeMethod方法,[传 "张三" 11 参数(可变长度)] 返回值为returnValue
Object returnValue = doSomeMethod.invoke(obj,"张三",11);
通过反射获取类的父类和父接口
Class c = Class.forName("java.lang.String");
//获取String的父类
Class superClass = c.getSuperclass();
System.out.println(superClass.getSimpleName());
//获取String实现的所有接口,返回Class数组
Class[] superInterfaces = c.getInterfaces();
for (Class superInterface : superInterfaces) {
System.out.println(superInterface);
}
注解
概述
• 属于引用数据类型
编译之后也是生成 xxx.class文件
语法格式:
修饰符列表 @interface 注解类名{
}
使用
• 注解使用的语法格式: @注解类型名
• 可以出现在 类上,方法上,变量上,注解类型上等
JDK内置的注解
• @Override
• 出现在方法上,供编译器检查是否为重写的方法
• 并不是必须的
• @Depercated
• 标注这个已经过时了, 有一个删除线
• 向其他程序员告诉已过时,有更好的解决方案
元注解
如果一个注解修饰一个注解类型, 那么他就叫元注解
常见的元注解
• Target
• 一个元注解,用来标注“注解类型”的注解
• 标注:被标注的注解可以出现在哪些位置上
• @Target({ElementType.METHOD}) : 表示被标注的注解只能出现在方法上
• Retention
• 表示该注解最终保存到哪(保持性策略)
• @Retention(RetentionPolicy.SOURCE) 表示该注解最后只会保存在java源文件中
• @Retention(RetentionPolicy.CLASS) 保留在class文件中
• @Retention(RetentionPolicy.RUNTIME) 保留在class文件中 并且可以被反射读取
在注解里面创建一个属性
public @interface MyAnnotation{
String name(); //属性 看着很奇怪,但就是这样
String color() default="默认"; //设置默认值
}
当使用这个注解的时候,需要这样使用(指定属性值)
@MyAnnotation(属性名=属性值,属性名=属性值)
public static void doSome(){}
如果属性名是value 是 String/int/… value() 那么在用这个注解的时候 直接 @MyAnnotation(“zhangsan”) value可以省略(只有一个value属性)
类型
byte short int long float double boolean char String Class 枚举
注解在开发时的作用
• 对程序的一种注释,一种标记
• 如果这个元素上有这个注解会怎么办,没有会怎么办
网络编程
什么是网络编程?
java提供的类库,可以实现顺滑的网络连接,联网的底层细节被隐藏,由JVM控制。并且java实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境;
计算机网络是什么?
把分布在不同地理区域的计算机与专门的外部设备用通信线路互联成一个规模大,功能强的网络系统,从而使众多的计算机可以方便的交互传递信息、共享硬件、软件、数据信息等资源。
网络编程的目的?
直接或间接的通过网络协议与其他计算机实现数据交互,进行通讯
网络编程中两个主要的问题
• 如果准确的定位到网络中的一台或多台主机:定位主机上的应用(IP和端口号)
• 找到主机后如何可靠并高效的进行数据传输(TCP/IP模型【应用层,传输层,网络层,物理+数据链路层】)
IP/端口号(网络通信要素1)
IP:一个主机的位置
端口号:一个主机上不同的应用程序
ip分类(了解)
• IP地址分类:ipv4和ipv6
• Ipv4:4个字节组成,4个0-255,大概有42个,30亿都在北美,亚洲4个亿。2011年初已经用尽,以点十进制表示:192.168.0.1
• Ipv6:6个字节组成(128位),写成8个无符号整数,每个整数用4个十六进制表示,数之间用冒号分开例如:3ffe:3201:1404:1280:c8ff:fe4d:db39:1934
• ip地址分类:公网地址(万维网使用)和私有地址(局域网使用)
192.168.0.0-192.168.255.255 为私有地址【192.168开头的都是私有地址】
InetAddress类
实例化InetAddress(返回一个InetAddress对象)
InetAddress.getByName("域名/IP地址") // 返回一个 InetAddress对象【静态方法】
InetAddress.getLoaclHost(); //得到本机IP地址【静态方法】
本机回路地址 127.0.0.1 == localhost
InetAddress对象的常用方法
getHostName(); //得到域名
getHostAddress(); //得到IP
端口号
用来表示计算机上正在运行该的进程(不同的进程有不同的端口号)
被规定为一个16位的整数 0~65535
• 公认端口
~1023 被预先定义的服务通信占用(Http占用端口:80,FTP:21 Telnet:23)
• 注册端口
1024~49151 分配给用户进程或应用程序(Tomcat:8080 MySQL:3306 Oracle:1521)
• 动态/私有端口
49152~65535
端口号与IP地址的组合得出来一个套接字(Socket 骚k t)
网络协议(网络通信要素2)
网络协议被分成好几层:应用层,传输层,网络层,物理+数据链路层
传输层中UDP和TCP的区别
TCP协议:
Ø 使用TCP协议前,先建立传输数据通道
Ø 传输前,采用“三次握手:方式,点对点通信,可靠。(三次握手,让双方知道 我在你也在)
Ø TCP协议进行通信的两个进程:客户端,服务端
Ø 在连接中可进行大数据量的传输
Ø 传输完毕,需要释放已建立的连接,效率低(四次挥手)
客户端(我要断开了)---->服务端(我接受到了)(我断开了)----->服务端(验证一下再发一条)
【QQ聊天】(打电话)
UDP协议:
Ø 将数据,源,目的地封装成数据包,不需要连接
Ø 每个数据报的大小为64kb内
Ø 发送不管对方是否准备好,接受方收到也不确认,所以不可靠
Ø 可以广播发送
Ø 发送数据结束时无需释放资源,开销小,速度快
【网络视频,直播】(发短信,发电报)
路径问题
获取文件绝对路径
以前
• FileReader reader = new FileReader(“thread/peizhi”)
• 这种路径的缺点是:移植性差,在IDEA中默认的当前路径是project的根
• 假设离开了IDEA换到了其他位置,可能当前路径就不是当前project的根了,此时路径无效
通用
• 只适合文件放在类路径下的
• 什么是类路径:凡是在src下的都是类路径下
• src是类的根路径
• String path = Thread.currentThread().getContextClassLoader().getResource(“classinfo.properties”).getPath();
解释:
- Thread.currentThread() // 得到当前线程对象
- getContextClassLoader() // 是线程对象的方法,可以获取到当前线程的类加载器对象。
- getResource() // 是类加载器的方法,当前线程的类加载器默认从类的根路径下加载资源。
适用于 linux系统
直接以流的方式返回
InputStream inputStream =
Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo.properties");
直接返回一个InputStream流
资源绑定器
java.util.ResourceBundle包下
实现
ResourceBundle resourceBundle = ResourceBundle.getBundle(“classinfo”);
通过key得到value
String value = resourceBundle.getString(“classname”);
// 得到对应的classname的value
注意
• 这里的后缀一定要去掉
• 只能绑定类路径下的配置文件(src下的 .properties文件)