本帖最后由 小石姐姐 于 2018-4-24 16:19 编辑
Java第二阶段day11-第三阶段01
#多线程:
- 进程:当前正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
- 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
- 运行程序:通过进程中的线程来运行。edg:公司接收项目,开发部门开发
##线程的实现:
###方式一:定义Thread的子类,是线程类
创建线程的步骤:
1.定义一个类继承Thread。
2.重写run方法。run方法里是线程执行的任务
3.创建子类对象,就是创建线程对象。
4.调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法
Thread
* final String getName() 返回该线程的名称。 不能被重写
* void setName(String name) 改变线程名称,使之与参数 name 相同。
*
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
mt1.setName("老王");
mt2.setName("老宋");
mt1.start();
mt2.start();
//线程子类中重写run方法
@Override
public void run() {
//任务
for (int i = 0; i < 100; i++) {
//此getName()认为父类对象在子类对象中,两者对外表现为同一对象,名字代表这同一对象的名字
System.out.println(getName() + ":" + i);
}
}
###方式二:实现Runnable接口,是任务类,Thread也实现了Runnable接口
*Runnable接口用来指定每个线程要执行的任务。只包含了一个 run 的无参数抽象方法,需要由接口实现类重写该方法。*
创建线程的步骤:
1. 定义类实现Runnable接口。
2. 覆盖接口中的run方法。
3. 创建Thread类的对象
4. 将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
5. 调用Thread类的start方法开启线程。
//新建一个任务
MyThread mt = new MyThread();
//两个线程共用一个mt,将任务放入线程,任务和线程分离开
Thread t = new Thread(mt);
t.setName("老王");
t.start();
Thread t1 = new Thread(mt);
t1.setName("张三");
t1.start();
//任务类中重写run方法
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//获取当前线程对象:任务在哪个线程执行,就返回哪个线程对象。
//Thread t = Thread.currentThread();
//System.out.println(t.getName() + ":" + i);
//链式编程
System.out.println(Thread.currentThread().getName() + ":" + i + num);
}
}
##两种方式的不同:
- 继承Thread类创建线程,在子类中固定了线程的任务
- 实现Runnable接口创建的是任务类,Thread类创建不同线程,将任务对象传入线程对象中(构造方法实现)
- 方式二更灵活,用的更多
---
##多线程安全问题产生和解决
产生:不同线程在各自run()方法中操作共享数据时,由于CPU的高速切换导致某一线程的run()方法会对其他线程操作共享数据产生影响
解决:解决不同线程操作共享资源,没有共享资源不需要安全机制
几种情况下的共享资源(售票为例):
- 三个线程操作同一对象,因此里面ticket是共享的。
- 三个线程操作同一类不同对象,Static ticket 属于类 因此所有对象共用一个ticket
- 定义在run方法中的不共享:线程t.start()执行任务对象中run方法,三个线程调用了三个run方法,i是局部变量超出方法外就死亡,因此是分别定义声明了三个i。
- ticket定义在一个对象中,在调用方法时,run方法操作ticket并没有自己定义
##解决方法:
synchronized(同一对象){}
###同步代码块:
1. 线程接收同一对象
//同一对象
TicketeThread tt = new TicketeThread();
//不同线程接收
Thread t1 = new Thread(tt);
t1.setName("窗口1");
Thread t2 = new Thread(tt);
t2.setName("窗口2");
Thread t3 = new Thread(tt);
t3.setName("窗口3");
// 定义共享的票,同一对象,run方法中没有自己定义,访问的时同一对象的tickete,所以tickete共享
int tickete = 100;
//同一对象中的obj为同一个对象
Object obj = new Object();
@Override
public void run() {
while (true) {
//同一个对象,同一把锁
synchronized (obj) {
if (tickete > 0) {
try {
// 明确观察出本程序的问题:售票出现重复和负数
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + tickete--);
}
}
2. 线程接收不同对象
TicketeThread tt = new TicketeThread();
TicketeThread tt2 = new TicketeThread();
TicketeThread tt3 = new TicketeThread();
//
Thread t1 = new Thread(tt);
t1.setName("窗口1");
Thread t2 = new Thread(tt2);
t2.setName("窗口2");
Thread t3 = new Thread(tt3);
t3.setName("窗口3");
// 定义共享的票 ,静态时不同对象共享票
static int tickete = 100;
//但是不同对象中obj对象不同,解决obj也定义为static,类不同对象共享此对象
Object obj = new Object();
@Override
public void run() {
while (true) {
//不同对象中,obj对象不同,锁不同,所以依旧会错误
synchronized (obj) {
if (tickete > 0) {
try {
// 明确观察出本程序的问题:售票出现重复和负数
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + tickete--);
}
}
}
}
###同步方法:
###同步代码块
*和同步代码块安全机制一样,一般将具有共享数据的代码锁起来*
-非静态方法:this作为锁
//非静态方法:this作为锁
public synchronized void method2() {
if (tickete > 0) {
try {
// 明确观察出本程序的问题:售票出现重复和负数
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + tickete--);
}
}
-静态方法:本类的class字节码对象作为锁
//对于静态方法,锁为本类的class字节码对象
public synchronized static void method() {
if(num>10) {
System.out.println(Thread.currentThread().getName()+"送第"+num+"份礼物");
num--;
}
}
##如何在使用同步方法是让`while(true)...mathod()`停止循环:
while (true) {
boolean flag = method(box);
//老师利用同步代码块,能够不出错结束循环
//同步方法如何让循环结束 ??
if(flag){
break ;
}
}
//同步方法
private synchronized boolean method(List<Integer> box) {
if (!bonusPool.isEmpty()) {
//抽奖
...
}else {
//奖池为空时再输出奖项
...
//返回标记,代表可以结束循环了
return true;
}
//不可以结束循环
return false;
}
##让没执行完的run方法提前终止的方法如下:
1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
2. 使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。
3. 使用interrupt方法中断线程。 当run方法执行完后,线程就会退出。
4. 但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想让循环永远运行下去,可以使用while(true){……}来处理。`但要想使while循环在某一特定条件下退出,`最直接的方法就是`设一个boolean类型的标志`,并通过设置这个标志为true或false来控制while循环是否退出。
5. 在下面代码中定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false.在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值.
package chapter2;
public class ThreadFlag extends Thread
{
public volatile boolean exit = false;
public void run()
{
while (!exit);
}
public static void main(String[] args) throws Exception
{
ThreadFlag thread = new ThreadFlag();
thread.start();
sleep(5000); // 主线程延迟5秒
thread.exit = true; // 终止线程thread
thread.join();
System.out.println("线程退出!");
}
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* Socket
* 套接字
* 用于描述IP地址和端口, 是一种网络编程机制, 通信的两端都会有Socket
* 网络通信3要素
* 传输协议 传送接收方式
* IP地址 具体设备
* 端口号 具体设备的某个具体服务(进程)
* 常见通信协议
* UDP
* 它是一种无连接的不可靠协议(发送端通过Socket发送包,接收端通过Socket接收包,操作包:查看代码后再次修正)
* 数据传输大小限制为64K(一个包)
* 不需要建立连接即可传输
* 数据发送速度快, 发送方只发送数据, 不检查接收方是否真正接收到数据, 所以数据有丢包的情况
* 这种方式适合实时性要求强的场合, 比如网络电话, 网络游戏等环境, 这种协议延迟很小
* TCP
* 它是一种需要建立连接的可靠协议(由于连接,具有了“通道”,可以使用流传输,服务端监听“通道”,获取Socket对象,操作Socket对象:查看代码后再次修正)
* 没有数据传输大小的限制
* 在传输前需要先建立连接(三次握手)
* 它的重发机制保证了数据传输的准确性, 但因为需要接收方发回验证信息, 所以数据发送时间长, 数据流量大
* 这种方式适合准确性要求强的场合, 比如金融系统, 视频点播, 用户可以等待数据传输但是不能忍受错误
## InetAddress概述和测试
* `java.net.InetAddress`类: 用于表示IP地址对象 (InetAddress的实例包含 IP 地址,还可能包含相应的主机名)
* 静态方法
* `static InetAddress getLocalHost()`: 获取本机的InetAddress对象
* `static InetAddress getByName(String host)`: 根据**主机名**或**IP的字符串**获取主机的InetAddress对象
* `static InetAddress getLoopbackAddress()`: 获取回环地址的InetAddress对象. 即`127.0.0.1`或` localhost`
* `static InetAddress getByAddress(byte[] addr)`: 根据IP获取InetAddress对象
* 如: `InetAddress.getByAddress(new byte[]{(byte)192, (byte)168, (byte)1, (byte)1});`
* 成员方法
* `String getHostAddress()`: 返回主机的IP地址
* `String getHostName()`: 返回主机名
## UDP协议发送数据
* `java.net.DatagramSocket`类: 基于UDP协议的Socket
* 构造方法
* `DatagramSocket()`: 创建DatagramSocket对象, 随机分配自己的端口号
* `DatagramSocket(int port)`: 创建DatagramSocket对象, 指定自己的端口号
* 成员方法
* `void send(DatagramPacket p)`: 发送数据包
* `void receive(DatagramPacket p)`: 接收数据, 数据保存在DatagramPacket对象中
* `void close()`: 关闭通信, 释放资源
* `java.net.DatagramPacket`类: UDP数据包
* 构造方法
* `DatagramPacket(byte[] msg, int msgLength, InetAddress host, int port)`: 创建数据包对象, 指定数据, 目标主机对象, 端口
* 用于发送端,不仅指定了封装数据的字节数组和数据的大小,还指定了数据包的目标IP地址(addr)和端口号(port)。
* 该对象通常用于发送端,因为在发送数据时必须指定接收端的IP地址和端口号,就好像发送货物的集装箱上面必须标明接收人的地址一样。
* `DatagramPacket(byte[] buf, int length)`: 创建数据包对象, 接收数据为length的数据, 存入byte数组中
* 这样的对象只能用于接收端,不能用于发送端。因为发送端一定要明确指出数据的目的地(ip地址和端口号),而接收端不需要明确知道数据的来源,只需要接收到数据即可。
* 成员方法
* `InetAddress getAddress()`: 获取数据包发送方的InetAddress对象
* `byte[] getData()`: 获取包中的数据, 以byte数组形式
* `int getLength()`: 获取数据包中数据的长度, 即byte数组的长度
* `int getPort()`: 获取发送方端口号
###大体原理:
DatagramPacket数据包的作用就如同是“集装箱”,可以将发送端或者接收端的数据封装起来。然而运输货物只有“集装箱”是不够的,还需要有码头。在程序中需要实现通信只有DatagramPacket数据包也同样不行,为此JDK中提供的一个DatagramSocket类。DatagramSocket类的作用就类似于码头,使用这个类的实例对象就可以发送和接收DatagramPacket数据包。
## UDP收发数据注意事项
* 端口绑定异常:
* `java.net.BindException: Address already in use: Cannot bind`: 端口已经被占用
* 如何解决: 要更改为其他端口号, 或将占用端口的应用关闭
## TCP协议发送数据:
*TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接。*
*服务端和客户端通过套接字进行交互,套接字相当于文件因此内容具有格式,需要刷新,读取同理*
###客户端:
* `java.net.Socket`类: 基于TCP协议的Socket, 作为客户端
* 构造方法
* `Socket(InetAddress add, int port)`: 创建TCP客户端对象
* 成员方法
* `OutputStream getOutputStream()`: 获取输出流对象, **用于发送数据**
* `InputStream getInputStream()`: 获取输入流, **用于接收数据**
* `void close()`: 释放资源
* TCP发送数据步骤
```java
// 1. 创建客户端Socket对象(建立连接)
Socket socket = new Socket(InetAddress地址, 端口号);
// 2. 获取输出流对象,返回将字节写入此套接字的输出流。字节在套接字上缓冲。目的地:Socekt
OutputStream outputStream = socket.getOutputStream();
// 3. 发送数据
outputStream.write(byte数组);
// 4. 释放资源
socket.close();
```
* 连接失败异常:
* `ConnectException: Connection refused: connect`: 连接被拒绝, 无法创建连接. 一般是因为网络不通, IP地址不存在等导致无法连接
* 解决办法:
* 确认必须有接收端, 且可以连接
## TCP协议接收数据
###服务端:
* `java.net.ServerSocket`: TCP服务端
* 构造方法
* `ServerSocket(int port)`: 创建一个TCP服务端, 并监听指定端口
* 成员方法
* `Socket accept()`: 监听数据, 会阻塞. 收到数据后返回Socket对象
* `void close()`: 关闭Socket
* TCP接收数据步骤
```java
// 1. 创建服务端ServerSocket对象:
ServerSocket serverSocket = new ServerSocket(端口号); // 指定要监听的端口号
// 2. 监听数据,侦听并接受到此服务器套接字的连接。返回新的Socket套接字(实现客户端和服务端的连接)
Socket socket = serverSocket.accept(); // 该方法会阻塞, 直到有客户端连接
// 3. 获取输入流对象,返回从此套接字读取字节的输入流。数据源:Socket
InputStream inputStream = socket.getInputStream();
// 4. 获取数据
int dataLength = inputStream.read(装数据的byte[]数组);
String s = new String(byte[], 0, dataLength);
// 5. 输出数据
System.out.println(s);
// 6. 释放资源:
socket.close();
serverSocket.close();
```
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
#第1章 反射机制概述、字节码对象的获取方式、反射操作构造方法、成员方法、成员属性
##反射机制的概述 字节码对象的获取方式:
1. Reflect。Java反射机制是在 `运行` 状态中:对于任意一个:
*类:都能知道这个类的所有属性和方法(包括私有)。
*对象:都能够调用它的任意一个方法(包括私有)。
这种`动态获取`的以及`动态调用对象的方法`的功能称为Java的反射机制
2. 反射目的:
*反射可以在不改变源代码的情况下...
3. 反射的前提:
* 要获取类的字节码对象(Class对象)
- 字节码对象的获取方式(三种):
---
##反射操作构造方法:
`字节码对象` -->`构造方法对象`-->`通过构造方法创建对象`
通过反射获取构造方法并使用:
*Class
`Constructor<?>[] getConstructors()` :得到`无参的构造方法对象`数组
`Constructor<T> getConstructor(Class<?>... parameterTypes)`:
- 根据不同参数列表区分不同构造方法,返回有参的构造方法对象
- `本方法参数列表` 为 `构造方法参数列表` 的class对象(主要是为了区分)
`T newInstance()` 只能用于无参,一步创建具体类的对象
*构造方法以对象形式进行返回(属性、方法也是)
*Constructor:
`T newInstance(Object... initargs)`:
根据具体参数返回具体的JavaBean对象,参数类型和得到构造方法对象时的参数列表一致(主要是为了初始化)
---
##反射操作成员变量
`字节码对象` -->`获取JavaBean对象`-->`成员变量对象`-->公共:`非静态变量值跟随具体对象,使用时需要传入具体对象`
| |_私有:多一步:setAccessible让jvm不检查权限
|_`构造方法对象`-->`通过构造方法创建对象`
|_ `class一步获取`
通过反射获取成员变量并使用
* Class:
* `只能获取public修饰的,其他三种均不可,可以直接操作`
* Field[] getFields() :返回一个存储`公共成员变量对象`的数组
* Field getField(String name) :返回`指定名称`的`公共成员变量`对象
* `能获取所有权限的,私有成员操作需要多一步`
* Field[] getDeclaredFields() :返回一个存储`所有权限的成员变量对象`的数组
* Field getDeclaredField(String name) :...返回`指定名称`的成员变量对象
*
* Field:
* 公共(直接使用以下下方法):
* 私有(通过setAccessible(boolean b)让jvm不检查权限,再使用以下方法):
*
* Object get(Object obj) :返回`成员变量对象`跟随的`JavaBean对象(具体)`的值
* void set(Object obj, Object value) :设置`成员变量对象`跟随的`JavaBean对象(具体)`的值
------------------------------------------------------------------------------------------------------------------------------------------
#HTML概述:
概念:
HTML:Hyper Text Markup Language --- 超文本标记语言.
* 标记语言:指的是通过一组标签的形式描述事物的一门语言.
* 超文本:比普通的文本更强大.
作用:
HTML是用来制作页面(静态页面).
*静态页面:假的数据,不是从数据库动态取得的
应用场景:
在设计网站的页面的时候都要使用到HTML.
小结:
- HTML学习的就是标签,具有结构性(标签表现)
- html文件被浏览器解析运行(java文件被JVM解析运行)
- 标签的使用:
* <标签 属性 = "" 属性 = "">
标签元素:<元素标签></元素标签>
</标签>
* 不需要包围文字的标签可以:<标签/>使用
* 信息被标签包围,属性在标签内,用来设置信息
##HTML的字体标签:
<font>标签:HTML中的字体标签.使用:<font>文字</font>
*三个属性 color颜色 size字体大小 face字体类型
*字体颜色:
英文单词
配色方案(Java程序员不推荐) :红绿蓝 16进制 :#FFFFFF两位一个颜色,组合配色
*字体大小:1-7 超过按最值算
##HTML的排版标签:
*一类标签*
标题标签:h标签<h1>...<h6> 从h1-h6分为六个等级的标题
段落标签:<p>内容</p> 包围的为一段
B I U :字体加粗 字体斜体 字体下划线
居中标签:<center>内容</center>
#HTML的图片标签:
重点:图片标签`<img/>` 属性多
* 属性:
* src :图片的来源.
src 相对路径,相对位置:先找自己在哪,再找目标
../ 上一级
* width :图片的宽度.
* height:图片的高度.
* alt :图片找不到显示的内容.
##【HTML的列表标签】:
有序列表
<ol>
<li>列表</li>
...
<li>列表项</li>
</ol>
有序列表的属性:
* type属性:
* 1 :数字类型
* a :英文类型
* i :罗马字符
* start属性:从哪开始
无序列表(unlist?)
<ul>
<li>列表项</li>
...
<li>列表项</li>
</ul>
无序列表的属性:
* type属性
* disc :实心点.
* circle :空心圆
* square :方块.
##【HTML的超链接标签】
HTML的超链接标签:<a>
属性:
* href :链接的路径(跳转的地方)
* target :打开的方式
* _self :在自身页面打开
* _blank :新打开一个窗口
* _parent :在父元素页面打开(需要框架支撑)
#【HTML的表格标签】
重点:
- 表格:很规整(牵一发而动全身)
- 创建时根据最大生成总表格大小
- 不能单独只把一个单元格改变大小,会改变牵扯到的其他单元格
- 在单元格中操作,不能操作行
-
- <td></td> 相当于一个单元格:属性:colspan||rowspan 自己占几格
- 百分比:相对于父类标签
表格的嵌套(在单元格中),表格单元格的合并
表格:
<table>
<tr>
<td></td>
<td></td>
</tr>
</table>
属性:
width :表格宽度
height :表格高度
border :边框
align :表格水平位置:
* left
* center
* right
<td>的属性:
* colspan=”列数”
* rowspan=”行数”
#【HTML的表单标签(*****)】:
表单元素提交需要 :
name 属性
type
value值
HTML的表单标签:<form>
* 常用属性:
* action属性:提交的路径.默认提交到当前页面
* method属性:请求的方式.GET和POST.默认是GET.
***** GET方式和POST方式的区别?
* GET :数据会显示到地址栏中.GET方式提交是有大小的限制.
* POST :数据不会显示到地址栏中.POST方式提交的是没有大小限制.
HTML中表单元素:
* <input type=”text”> :文本框.
* 常用属性:
* name :表单元素的名称.必须有name属性,然后后台才可以接收数据.
* value :文本框的默认值.
* size :文本框的长度.
* maxlength:文本框输入的最大长度.
* readonly:只读文本框.
* <input type=”password”> :密码框.
* 常用属性:
* name :表单元素的名称.必须有name属性,然后后台才可以接收数据.
* value :密码框的默认值.
* size :密码框的长度.
* maxlength:密码框输入的最大长度.
* <input type=”radio”> :单选按钮.
* 常用的属性:
* name :表单元素的名称.必须有name属性,然后后台才可以接收数据.
* value :单选按钮的默认值.
* checked:单选按钮默认被选中.
* <input type=”checkbox”> :复选按钮.
* 常用的属性:
* name :表单元素的名称.必须有name属性,然后后台才可以接收数据.
* value :单选按钮的默认值.
* checked:单选按钮默认被选中.
* <input type=”button”> :普通按钮.没有任何功能的按钮.
* <input type=”submit”> :提交按钮.
* <input type=”reset”> :重置按钮.
* <input type=”file”> :文件上传的表单项.
* <input type=”hidden”> :隐藏字段.
* <input type=”image”> :图片按钮
* <select> :下拉列表.
* <textarea> :文本域.
|
|