0%

2017年7月3日 上午8:00

TCP:传输控制协议
UDP:数据报文协议

端口 :16位

三类

  1. 公认端口:0~1023
  2. 注册端口:1024-49151
  3. 动态和、私有端口:49151~65535

常用端口:

Telent:23
Stmp:25
Ftp:21
Http:80

常用对象:

ServerSocket

  1. ServerSocket(port):创想serverSocket实例
  2. accept()等待客户端连接
  3. Public InetAddress getInetAddress()返回服务器的Ip地址
  4. Public boolean isCloseed();赶回ServerSocket的关闭状态
  5. Public void close();关闭ServerSockets

Socket

  1. Socket(String host,int port):构造socket对象,同时,指定要连接服务器的主机名和端口号
  2. public inputStream getInputStream:返回套接字的输入流
  3. pubilc OutputStream .getOutputStream()返回套接字的输入流
  4. Public boolean isClosed()判断套接字是否关闭
  5. Public void close():关闭socket

InetAddress

  1. public static InetAddress(String host):通过主机名或IP地址得到一个InetAddress对象
  2. String getHostName();获取ip地址对应的主机名
  3. String getHostAddress();返回ip地址字符串

注:他们处理的都是字节流

使用PrintWriter的例程

网络编程的服务端步骤比较单一:
1. ServerSocket对象
2. 创建Socket对象
3. inputStream对象
4. BufferedReader对象
5. outputStream对象
6. printWriter对象
7. 读
8. 写
注:他们都是一条流水线,前一个对象产生下一个对象
例程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server1 {

public static void main(String[] args) throws IOException {

//创建服务端实例对象,需要指定端口

ServerSocket ss = new ServerSocket(7799);

//接受客户端连接,创建socket对象

Socket socket= ss.accept();

//获取输入流,读取客户端传过来的数据
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));

//输出流 ,向客户端返回数据
PrintWriter pw= new PrintWriter(socket.getOutputStream());

//服务端读
String content = br.readLine();
if(content != null){
System.out.println("获取到的数据是:"+content);
}

//服务端写
pw.write("服务端收到数据");

//倒序关闭
pw.close();
br.close();
is.close();
socket.close();
ss.close();
}

}

网络编程的客户端步骤比较单一:
1. 创建Socket对象
2. outputStream对象
3. printWriter对象
4. inputStream对象
5. BufferedReader对象
6. 写
7. 读
例程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {

public static void main(String[] args) throws UnknownHostException, IOException {
//创建socket对象,连接服务端
Socket socket = new Socket("127.0.0.1",7799);

//写对象
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);

//读对象
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));

//客户端写
pw.write("我要访问服务端");
pw.flush();
socket.shutdownOutput();//确保数据传输到服务端


//客户端读
String result = br.readLine();
if(result != null){
System.out.println("获取到的数据是:"+result);
}
br.close();
socket.close();

}

}

注:

  1. 他们都是一条流水线,前一个对象产生下一个对象
  2. 客户端和服务的不能同时创建输入流
  3. 这里socket.shutdownOutput的作用
    1. 【TCP】s.shutdownOutput();这行代码的牛逼之处 - 闲着没事_玩玩JAVA的专栏 - 博客频道 - CSDN.NET
    2. 第一种方法close流,相当于给流中加入一个结束标记-1
    3. 第二种方法:s.shutdownOutput();关闭客户端的输出流。相当于给流中加入一个结束标记-1.
    4. 就像字符串的结尾一样
    5. 服务端没写socket.shutdownOutput,用pw.close代替了

错误:没有打开服务端就开启客户端

扩展:
以上代码使用
DateOutputStream 和 DateInputStream也可以

使用对象流的例程:

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package package1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {

public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1",7799);

//pet对象
Pet pet = new Pet("狗",1);
//写对象
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
//读对象
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//写(对象流不用shutdown,不知道为啥)
oos.writeObject(pet);
oos.flush();

//读
String string = br.readLine();
if(string != null){
System.out.println("接收到的信息是:"+string);
}

br.close();
is.close();
oos.close();
os.close();
socket.close();


}

}

服务端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package package1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {

public static void main(String[] args) throws UnknownHostException, IOException {
Socket socket = new Socket("127.0.0.1",7799);

//pet对象
Pet pet = new Pet("狗",1);
//写对象
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
//读对象
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//写(对象流不用shutdown,不知道为啥)
oos.writeObject(pet);
oos.flush();

//读
String string = br.readLine();
if(string != null){
System.out.println("接收到的信息是:"+string);
}

br.close();
is.close();
oos.close();
os.close();
socket.close();
}
}

Pet类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package package1;

import java.io.Serializable;

public class Pet implements Serializable{
private String name;
private int id;

public Pet(String name, int id) {
super();
this.name = name;
this.id = id;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}

错误:Pet类没有序列号接口: implements Serializable

服务器常开例程(死循环方式)

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package package2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {

public static void main(String[] args) throws UnknownHostException, IOException {
// Socket socket = new Socket("192.168.12.35",8800);
Socket socket = new Socket("127.0.0.1",7799);

//pet对象
Pet pet = new Pet("狗","狗","男");
//写对象
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
//读对象
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//写(对象流不用shutdown,不知道为啥)
oos.writeObject(pet);
oos.flush();

//读
String string = br.readLine();
if(string != null){
System.out.println("接收到的信息是:"+string);
}

br.close();
is.close();
oos.close();
os.close();
socket.close();


}
}

pet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package package2;

import java.io.Serializable;

public class Pet implements Serializable{
/*
*
*/
private static final long serialVersionUID = 1L;
private String name;
private String type;
private String sex;


public Pet(String name, String type, String sex) {
super();
this.name = name;
this.type = type;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}


}

服务端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package package2;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

public static void main(String[] args) throws IOException, ClassNotFoundException {


while(true){
ServerSocket ss = new ServerSocket(7799);
Socket socket = ss.accept();

//输入流
InputStream is = socket.getInputStream();
ObjectInputStream br = new ObjectInputStream(is);

//输出流
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);

//输入
Pet pet = (Pet) br.readObject();

if(pet != null){
System.out.println("name : "+pet.getName()+"id :"+pet.getSex());
//文件写入
FileWriter fw = new FileWriter("hello_pet.txt");
BufferedWriter bw = new BufferedWriter(fw);
bw.write("name : "+pet.getName()+"id :"+pet.getSex());
bw.flush();
bw.close();
}
//输出
pw.write("客户端已得到pet对象");


pw.close();
os.close();
br.close();
is.close();
socket.close();
ss.close();
}
}

}

注:
1. 通过换host可以实现局域网下访问,要有以下两个条件
1. 但是需要有相同的序列号这里使用的是IL
2. Pet所在的包要相同

服务器常开例程2(死循环方式+Thread)

服务端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/*
* 宠物店 服务器端
* */
public class PetServer {
public static void main(String[] args) {
try {
//1.建立一个服务器Socket(ServerSocket)绑定指定端口并开始监听
ServerSocket serverSocket=new ServerSocket(8800);
//2.使用accept()方法阻塞等待监听,获得新的连接
Socket socket=null;
//记录注册宠物的数量
int num=0;
//一直处于监听状态
while(true){
socket=serverSocket.accept();
ServerThread serverThread=new ServerThread(socket);
serverThread.start();
num++;
System.out.println("有"+num+"只宠物在本店注册!\n");

//获得客户(宠物主人)的IP信息
InetAddress ia=socket.getInetAddress();
//获得ip
String ip=ia.getHostAddress();
System.out.println("本宠物主人的IP地址为:"+ip);
//获得主机名
String name=ia.getHostName();
System.out.println("本宠物主人的主机名为:"+name);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

Thread类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

/*
* 专门的线程类
* */
public class ServerThread extends Thread {
//和本线程相关的Socket
Socket socket=null;

public ServerThread(Socket socket){
this.socket=socket;
}

//线程启动:响应客户请求
public void run(){
try {
//获得输入流
InputStream is=socket.getInputStream();
//获得流:可以对对象进行反序列化
ObjectInputStream ois=new ObjectInputStream(is);
//获得输出流
OutputStream os=socket.getOutputStream();
PrintWriter pw=new PrintWriter(os);
BufferedWriter writer = new BufferedWriter(new FileWriter(new File("pet.txt"), true));
//4.读取宠物信息,将宠物信息保存到文件中去
Pet pet=(Pet)ois.readObject();
writer.write("----宠物注册信息----");
writer.write("姓名:"+pet.getName());
writer.write("品种:"+pet.getType());
writer.write("性别:"+pet.getGender());
writer.flush();

//给宠物主人一个响应
String reply="恭喜,您的宠物已经在本店注册成功!";
pw.write(reply);
pw.flush();
//关闭资源
pw.close();
os.close();
ois.close();
is.close();
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

比较两种服务器常开的:

1.     第一种的思路是:
    1. 服务器整体写一个循环,这个循环中包含全部的内容,ServerSocket对象和以及想的close()
    2. 这个种方法操作流程是:一个客户端访问完,这个循环走到accept()方法处,等待下一次客户端的访问(server就是一个苦力,真可怜个)。
2. 第二种的思路是:
    1. 首先是写一个循环,循环的作用是让服务器不停止运行,然后,写一个Thread类
    2. 这个类的入口是:accpet()方法产生的socket对象
    3. 执行的内容是:获取客户端内容,然后给一个回馈    
    4. Thread类写完之后,最后在while()循环中开启Thread的一个线程,并计数
    5. 这种方法执行的流程是:整个过程可以看成是一个生活场景。Server.class就像一个前台人员,她的主要任务有两个:一个是迎接来宾(client.class的访问),一个是命令他的小弟Thread的对象去完成当前用户的要求,然后她接着笑脸迎接下一个用户(boss就是boss,美女会笑就行,她其实也没干啥)

3. 区别:
    1. 在每次一个client访问模式下,这两种是没有区别的
    2. 但是,当多个client并行访问时,第一种是无法实现的,因为所有的任务都是他一个人来执行,干完一个才能干下一个。第二种是可以实现的,在这种情况下有一个团伙,美女boss管理者一堆小弟

两种客户端和服务端交互方式,其实都行,不过习惯使用第二种:

第一种:BufferedWriter

1
2
3
4
5
6
OutputStream  is2 = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(is2));

bw.write("name : "+pet.getName()+"id :"+pet.getSex());
bw.flush();
bw.close();

第二种:使用PrintWriter

1
2
3
4
5
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);

pw.write("客户端已得到pet对象");
pw.close();

注:细节中应该还是有区别的,没有仔细的测试!

2017年7月3日 下午4:55

这篇文章的思路是:
1. 文章产生的原因
2. 我总结出来的“框架”
3. 对框架的说明
1. 其中有哪些注意点
2. 原因
3. 如何解决
4. 例子

第二版改进:

1. 加了一个原则:


2. 封装加了一个

3. 包的设计

项目难度分类
1. 数据库设计:我做过的就是报表系统,要将表设计成数据库形式,设计难,设计出来一般性能也很差!
2. 逻辑:项目中涉及到算法,一般的项目中没有
3. 业务流:这的难点是你需要了解一个行业的运行流程,而且要人打交道
4. 扩展性:你的老板老改需求,这扩展性我就得就是个梦!
5. 性能:I don’t care and I have no idea.
6. 总结:核心还是在于与业务流的熟悉和了解

这篇文章产生于这样一个问题:设计一个系统,我们应该如何设计?

于是我断断续续的想了两天,想出一个所谓的我的设计框架(这个名字我也是醉了,估计是神盾局特工看多了)

框架:其实就是能解决某功能的方法有哪些


注:这个框架会随着经验不断更新

说明

1.     我总结出来的最终方法很笨:**根据功能一个个的照着框架,从上向下试。**
    1. e.g.:一个图书管理系统的超时扣费功能,他不是一个属性、一个表,就可以完全解决的,所以就需要代码逻辑来解决,而这样的功能我们要如何封装,他和其他类之间的关系是如何才是设计的重点
2.  设计时有两个原则:也是容易犯的错误
    1. **不是框架中的所有元素都要在一个系统中使用**
        1. 不是所有的系统都要使用数据库:数据库只是只是只是一个数据存储的地方而已。比如说你用java设计一个权限系统,就必须用数据库吗?当然不是,权限系统的核心是分配权限的方式,只有需要保存数据时,才需要。权限系统其实可以先设计出来结构,编码实现,然后再去往上加数据库功能,这样也是可以的。
    2. **以功能为导向**也就是需求,**业务流的重要性**
        1. 我们要拿上一个具体的功能,去想他如何去实现,而不是上来画数据库ER图,类图。
        2. 首先要确定的就是功能,一点一点的列出来,然后在思考
        3. 每个功能想的差不多,最后在要将功能进行合并
    3. **只有知道了具体的每一部分,我们才能去动手**
        1. **这里会用到大约1/3的时间**
        2. 否则一定会返工
        3. 这里包括:
            1. 数据库的ER图
            2. 类图
            3. 包有哪些,他们的作用有啥
            4. 使用了哪些设计模式,为什么要使用
            5. 等....
        4. 在这个阶段容易的就是少考虑了某些情况,想到之后你又不知道怎样往里面插
3. 具体去讲解每一部分:
    1.  **数据库**:是为了保存数据,如果不保存也可以实现相同的功能,要他还干啥
        1. 我曾经做项目的时候为了彰显一下我数据库的不错,逻辑就不在代码中写了,而是直接写在了数据库中,但是到了后来,我发现很多写的数据库中并不合适,首当其冲的就是改起来太麻烦了。
        2. 但是有些功能,如一些自动的时间处理,你需要系统在规定的时间完成特点的功能,这时候在数据库去完成就比较合适了。
        3. 还有一个例子是:我见过有人(逗比石)的sql竟然运行起来是按分钟算,我也是醉了,我就得如果要放在数据库中解决的话,可以让数据库在特定的时间,来自动处理这些数据,放到一个表中,然后用的时候我们就之间查新表就行,不用从头开始再查了
    2. **封装**:封装的目的为了让程序有段落感,可以从宽泛到具体来设计系统,尤其是一些大的系统,写的写的就忘了前面的写的啥了,但是如果封装到位,
        1. **项目做起来就是一个功能一个功能的稳步推进,自己心中也有数**。
        2. **设计时就像一个一个个黑盒一样的感觉**。    
        3. 注意当前功能(方法)归属于那个类
            1. e.g.:现实中book是没有结账功能的,而是结账的对象是book
            2. 但是,在编程中实现在book类中,确实是更加方便。
        4. **封装过程中犯过的毛病**:
            1. 测试类中方法不能太多
                1. 解决方法:可以想象这个函数代表的功能是谁的
            2. 测试类中主方法太长
                1. 用c写的习惯了,100行以内根本不认为多
                2. 而java中面向对象,**超过二十行**就觉得有点多了
            3. 构造函数顺序:
                1. 无参构造
                2. 主键构造
                3. 带所有参数的构造
            4. **注释注释注释注释**
        5.  对于框架而言我说几句废话,我是做一开始学的是php,php可以脱离框架完成的,但是我后面学了框架之后,让我想到了学长的话“框架就把人学废了”,框架做项目的确快很多,快到了即使一个人没学过php语法,我觉得给他一个星期去学框架这里指ThinkPHP,我就得也可以做出一个不错的门户网站。**学习阶段千万别依靠框架,废了你**。
    3.  **设计模式**:
        1. 目的一是**为了实现一些特定的功能**,必须使用某种模式才可以实现
            1. 根据**每种设计模式解决的问题来选择**,请看前面~设计模式~那篇文章
        2. 目的二是**为了程序员自己编码方便**
            1. 比如说工厂模式,不用它也可以,但是用了更方便
        3. 面向对象的那几个特性,我把他们包括在设计模式中

包的补充:


一个android的项目目录

老师推荐的java项目目录

注:关于包的设计可以参考文章看 第三方开源组件的技巧

2017年7月1日 上午9:30

1.继承

修饰符

修饰符:
本类 同包 子类 其他(不同包) 唯一的标准
private 🐶 是否在 同一个类
frendly 🐶 是否在 同包 默认的
protect 🐶 是否 继承
public 🐶 都

final

1. 方法          重写
2. 类              继承
3. 变量属性  修改 

(当变量为对象时,final对象不可以=new Object(),但是仍然可以改变这个对象的属性)

super()

1. super只能在写方法中
1
2
3
4
5
6
7
8
9
package package1;
import package2.A;

public class B extends A {
//super.a =2;//Syntax error on token "{", { expected after this token
public B(){
super.a =2;
}
}
2. super不在过构造方法时,可以不放在第一行
3. super不能访问父类private成员
4. super()
5. super(a,b) 调用带参构造
6. super.属性
7. super.方法

继承的重写 方法的重载 对比

重写的条件
1. 类型相同/父类返回类型的子类
2. 修饰符只能扩大,不能缩小
3. 方法名,参数列表相同
4. 构造方法不能重写

重写之后的方法调用:

1
2
3
4
5
pet dog = new dog();
dog.print();//默认调用子类的print方法(print方法,子类父类中都有)

pet dog = new dog();
dog.method();//不能调用子类特有方法(method 只有子类中有)

重载的条件
1. 同一个类中
2. 参数列表不同
3. 方法名相同
4. 和返回值,访问修饰符无关

instanceof

1. pet instanceof Dog 
2. instanceof 使用:对象.instanceof(父类/接口)

2.多态

多态概念+要求(多态的前提是继承)

多态概念:同一个引用类型,使用不同的实例而执行不同操作
父类存在的地方一般都能使用子类代替

1. 要素:
    1. 子类重写父类分方法
    2. 使用父类的类型
2. 使用的地方:
    1. 方法参数
    2. 方法返回值

对java继承的测试(通过this)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//父类
package package3;

public class Car {
private int site = 4; //座位数

Car(){
System.out.println ("载客量是"+site+"人");
}
public void setSite(int site){
this.site = site;
}
void print(){
System.out.print("载客量是"+this.site+"人");
}
}

//子类
package package3;

public class Bus extends Car {
Bus(int site){
//这三个的输出结果是一样的
//this.setSite(site);
//super.setSite(site);
setSite(site);
}
// public void setSite(int site){
// super.site = site;
// //this.site = site;
// }
}

//测试类
package package3;

public class Test {

public static void main(String[] args) {
// TODO Auto-generated method stub
Bus bus = new Bus(20);
bus.print();

}

}

总结:
1. 在继承中,如果子类没有重写父类的方法,那么子类是调用的是父类的方法
2. 这里的调用,是在父类中执行,而不是在子类中执行(原来理解的是在子类中执行)
3. 因为:继承下来的setSite()方法中写的是this,java有一下几种
1. 继承下来将this改为super,那么就无法操作父类中的私有方法,所以不可能
2. 子类中还是保持this,那么,this指代的是子类,所以不可能父类私有属性,所以不可能
3. 这个this还是指向的父类,那么,说明本身父类也是存在的,像C++一样,在产生子类对象的时候,父类对象其实也产生了,那么这个setSite()其实父类对象自己执行的
4. 这三个的输出结果是一样的
this.setSite(site);
super.setSite(site);
setSite(site);
5. 继承不是拥有这项技能,而是能让父类帮你干,你自己其实不会。

3.抽象

抽象类要求

1. 抽象类不能实例化  --- 因为没有意义
2. `Pet pet = new Dog() //pet是abstract`
3. 是对的  这只是一个指针指向 没有分配空间进行实例化
4. 抽象类中可以有0个或者多个抽象方法  
5. 子类中,抽象方法在子类中必须实现

接口

接口的要求

1. 接口不能实例化
2. 实现类必须实现接口的所有方法
3. 变量都是静态变量
4. 现类可以实现多个接口
5. 方法:public abstract void service();   不能用static
6. 变量:public static final  double PI = 3.14;

总结

  1. 现在对比下来继承绝对是老大,知识点多。
  2. 继承是 多态 + 重写 的前提
  3. 继承+抽象 +接口在生成对象比较
    继承:
    继承就不用说了,多态的上转型的概念理解的就像!

抽象:

1
MotoVehicle moto[] =new MotoVehicle[4];
1. **这个MotoVehicle是abstract类**,但是能用new,这个例子就充分的说明了,new != 分配空间。
2. 在这里,他仅仅说明了moto[]的长度
3. **对象初始化挪到了后面,为了集中说明一个知识点!**

接口:

1
2
3
4
5
6
7
8
9
10
11
public class Test1 implements Interface1 {
@Override
public void fun() {
// TODO Auto-generated method stub
System.out.println("fun");
}
public static void main(String args[]){
Interface1 t = new Test1();
t.fun();
}
}

对比三个发现:父类 抽象类 接口 这三个都可以
1. 直接声明对象
2. 调用对象的方法
3. 但是,不可以初始化对象本身(父类初始化自己)

错误

对象数组的初始化方法:
第一种:

1
2
3
4
5
6
7
8
MotoVehicle moto[] = new MotoVehicle[]{
new Car(1,"宝马1","红色",1000,20),
new Car(2,"宝马2","红色",1000,20),
new Bus(3,"金龙1","绿色",1000,"x"),
new Bus(3,"金龙2","绿色",1000,"xx"),
new Bus(5,"金龙3","绿色",1000,"xxx"),
new Ka(6,"Ka1","兰色",1000,100)
};
第二种:
1
2
3
for(int i = 0 ;i < n;i++){
moto[i]= new Car(**);//只能生成一种Car类型的
}

对象的强制转换错误:

1
2
Dog dog = (Dog)pet;//强制转换
(Dog)pet.dark();//这的是错的,之后转换了之后才可以进行特有dog方法的调用

2017年7月1日 下午7:28

和类名相同的普通方法

1
2
3
4
5
6
7
8
9
class Parent {
public void Parent() {
System.out.println("parent");
}

public Parent() {
System.out.println("parent");
}
}

interface变量 和 方法 的定义方式

1
2
3
4
5
6
7
8
9
10
11
public interface Interface1 {

final int i= 1;
static int j = 1;
int k = 1;
public final static int q = 1;

public abstract void a();
public void b();
void fun();
}

封装的步骤

1
使用Java实现封装,第一步是修改属性可见性来限制对属性的访问,第二步是创建赋值和取值方法,用于对属性的访问,第三步应该是( )。
	A.	使用赋值和取值方法访问属性
	B.	编写常规方法访问属性
	C.	在赋值和取值方法中,加入对属性的存取限制
	D.	编写main方法创建对象,调用赋值和取值方法访问属性

分析如下所示的Java代码,则选项中的说法正确的是(c )。

1
class Parent{
	public String name;
	public Parent(String pName){
		this.name = pName;
	}
}
public class Test  extends Parent {  //1
	public Test(String Name){               //2
		name="hello";          //3
		super("kitty");          //4
	}	
}
	A.	第2行错误,Test类的构造函数中参数名称应与其父类构造函数中的参数名相同
	B.	第3行错误,应使用super关键字调用父类的name属性,改为super.name="hello";
	C.	第4行错误,调用父类构造方法的语句必须放在子类构造方法中的第一行
	D.	程序编译通过,无错误

2017年7月1日 下午3:16

这篇文章就是我边想问题边写的,记录了我整个思考的过程

几个注意点:
1. 整体的结构要心中有数,可以用图来辅助
2. 这个项目的难点在于功能流程的设计,数据库表的设计很简单
3. 发现隐藏的功能,做好以后功能的扩展。
4. 同时也要学会偷懒技巧,实现简单,但是却让人喜欢

现在想到的功能有:
1. 登陆注册,人员的流动 一天
1. 多用户登陆
1. 一个多线程的服务端
2. 一个客户端
2. 前后端两个
3. 会员管理系统
2. 权限管理系统:
1. 前后端可以设计不同的阶梯,CCF 一天
3. 图书查询系统【核心】: 三天
1. 用户查书有没有,模糊查询
1. 按作者
2. 按类别
3. 按日期查询
4. 按出版社
5. 按库存
2. 用户查自己的借阅情况
1. 用户还书
2. 超时扣钱
3. 管理员
1. 查询一本书的借阅历史
2. 查破损

4. 辅助功能【看时间自由调整】:                                                  一天
    1. 图书的破损,下架,丢失

程序员可以创建类
而用户只能创建对象


老师的那个是权限于功能进行了绑定
而我的这个是权限和功能是分开的,先判断权限再去执行功能。
但是功能从哪来呢?
那还不如最小的权限绑定功能,而角色和权限的多对多功能,这是操作对象,所以能够给了用户来操作,用户和角色的一对多的关系也是对象操作,也可以给了用户


设计模式:

不能为了使用设计模式,专门去向哪里可以用,
功能为导向哦!



这里的额priority1-3不能出现方法的重名

为了以后扩从管理员的功能:这里使用了代理模式
priority1是priority3针对interface3种方法的扩充
interface1,interface2是横向扩充
interface3 是纵向扩充


如何实现一个权限分多级的情况?

在interface中写不同的方法e.g.:input1() input2()
这里的权限1,2就是权限等级
这里的核心问题就是方法的参数化:利用反射可以解决
使用方法:
User中有一个方法userMethod(String methodName);
实现类似于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 反射中方法的测试
import java.lang.reflect.Method;

public class Test_Method {
public static void main(String[] args) throws Exception{
//获取Student对应的Class对象
Class cla=Student.class;
//创建Student对象
Student p=new Student();
//得到setName方法
Method met1=cla.getMethod("setName", String.class);
//调用setName,为name赋值
met1.invoke(p, "Jack");

//得到setName方法
Method met=cla.getMethod("getName", null);
//调用setName,为name赋值
Object o=met.invoke(p, null);
System.out.println(o);
}

}

其实这里可以使用组合模式
将Role继承自priority
好处是:要是用户只有一个Priority,一步就可以完成了,省去了Role。


如何实现上下级批准:

这个不属于类的设计,是通过逻辑代码实现的。
要是在类图中就实现上下级,那是一定不可改变得。

以及实现人员的变动,这都是通过逻辑代码实现的。


服务端的任务—其实是个假的服务端,因为他仅仅完成了登陆这一个功能:

1. 服务端就是一个黑盒
2. 不走权限整个流程
3. 接收的是用户名和密码
    1. 实现判断
    2. 实现注册
4. 返回的是
    1. 登陆是否成功
    2. 注册是否成功
    3. Flag标志区分普通用户和管理员
5. 实现普通用户和管理员的区分
    1. 查不同的文件txt

客户端:

1. 拿到用户名密码
2. 给了服务器
3. 根据服务器的返回,执行不同的普通用户和管理员的方法
4. 运行不同的操作(界面)

如何确定当前用户的操作?并执行?

现在是像洋葱一样,一层一层的,要用的对象(priority对象)在最里面。
User的useMethod()方法中的Class.from()这里写啥?
如果要是表中:用户 <—>priority
那Role怎么办?
Role是程序写的,这里就要实现其中包含的priority的全部方法。

啥是活的啥是死的?

程序员:
1. 增加一个priority类别,增加一个role类别
用户:
1. 在当前已有的priority 和 role中进行选择

又绕回到了一开始的那就话:

用户是不能去创建一个类,或者创建一个类中的方法的。
这些都是程序写死的,用户最多只能选择!


人工智能


能不能先写逻辑,再写图形界面?

1. 数据获取的方式发生改变。
    1. 原来是scanner
    2. 现在是JFrame的txt框
2. 页面跳转的方式放生了变化
    1. 原来是输入数字选
    2. 现在是鼠标点
3. 数据的输出方式了改变
    1. 原来是printf
    2. 现在是JFrame中

总体来说:填了很多麻烦

先写逻辑,后写图形

类有哪些?

1. 上面的权限,一堆
2. 还有book,bookBiz
3. 文件操作的FileBiz

有哪些包?

1. entity.book
    1. 计算总价方法
    2. 显示图书信息方法
2. bookBiz
    1. 初始化书的信息
    2. 图书入库
    3. 图书出库
    4. 新增图书
    5. 查询图书
    6. 购买图书
    7. 查询图书是否存在
3. entity.user
    1. 从priority获取方法名数组【长度规定为10】
    2. 显示当前用户所具有的功能
    3. 建立数字和方法的对应关系
    4. 根据输入,通过反射调用不同的功能
    5. 这里的输入之后,还得进行一个字符串拼接,实现权限分级
4. priority
    1. 调用bookBiz
    2. **获取方法名的数组**
5. priority.ext
6. priority.impl
7. file
8. file.impl
    1. 获取一行数据
    2. 取一行数据,分割到数组
    3. 取全部的数据,保存数组,除了第一行
    4. 插入表头
    5. 插入一条数据
    6. 新建一个文件
9. Test
    1. 给client数据
    2. 调用user完成工作
10. Server
    1. Thread
    2. 干活的类
11. Client
    1. 从test拿到数据给server

步骤?

1. 文件
2. book
3. 权限
4. server
5. user
6. test

把这个权限打成一个jar包去使用
这个现在还不考虑


权限

我知道的:
1. 类名
两种解决方法:
1. 给User建子类,每回登陆通过判断类名,好多的if产生不同的对象
1. 每次增加一个类,都要两边写
2. 试用反射,用类名 把类先反射出来(通过反射来读类
1. 这时已经能调用方法了
2. 能调用方法,那所有的问题都解决了


新的问题

1. **方法知道了,但是参数不知道**
    1. 在写一个参数的方法
    2. 其重要有参数的个数 和 类型
2. **invoke返回的是object对象**,除了string 和 int 类型
    1. 其他的类型是固定的没几种
        1. string[]
        2. string[][]
        3. ArrayList<String>

三个方法返回数组中的内容是有顺序的,必须一致
这个可以抽象成一个方法
3. 如何融入权限分层
1. 要限定
1. 一共三层
2. 方法要用method_1(),method_2();固定的_来分割
3. 把取出来的方法分割

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Class c=Class.forName("cn.chenzhiheng.user.priority.PriorityClient");
Object o=c.newInstance();
Method[] m=c.getMethods();
for(int i=0;i<m.length;i++){
System.out.println(m[i].getName());

if(m[i].getName().equals(method)){
Object[] os=new String[1];
ArrayList<String> strings=(ArrayList<String>) m[i].invoke(o,null);
for(int j = 0 ;j < strings.size();j++){
System.out.println("***"+strings.get(j));
}
}
}

Test的操作流程

1. scanner **获取用户名和密码+userId**
2. 通过client传递给server
    1. 这里要区分增删改查
3. 接受server传递过来的用户权限
    1. 不一定是权限
    2. 有可能是操作是否完成
4. 根据用户权限调用user打印出当前所有的方法
5. 调用方法去完成一些操作

当加上界面的时候

1. 界面是点的操作
2. 而控制窗是从上到下的一个顺序操作
3. 所以,**控制窗与逻辑的交互**由一个分割好的**test来完成**

三个数据表增删改查之间的关系

1. 买书的时候要增加info
2. 不删除book和user 只更改他们的状态
3. 退货删info
    1. 输入infoId
    2. 通过infoid找到bookid
    3. 删除info
    4. 更改bookid对应的数量
4. **user_book不直接操作,全部由Book类来操作**

怎样实现自增

1. 抽象类 static index
2. index++;
3. 构造函数

权限的升级:

1. 已有权限
    1. 直接更改用户的priority字段
    2. 用户权限的更改只能有管理员进行
2. 没有的权限
    1.有可能 **写Biz**
    2. 写priorityImpl
    3. 写priority继承自 ext 实现impl接口
    4. 然后在写一个role在包括原来的和现在的对象
    5. 在像上面一样更改用户权限字段

Biz的功能不是priority 都必须用了e.g.:insertHead。
程序员用一次就行了


这回幸亏是从下往上做,每一层都是一个封装。
对下层的多次运用


如何拿到使用不同类型反射回的结果

1. 由于多态中不支持多返回的类型
2. 所以这种情况,只能写不同的方法

结算功能:
一个用户-一个权限-一个Userbiz(多个唯一的biz)


理想中的代码层次-应该像:MVC
1. 我从数据库中取出用户的权限等级,作为全局变量
2. 然后生成对应这个等级的页面
3. 点击一个功能按钮,然后跳到逻辑处理层
4. 逻辑处理层的数据,然后投影到一个页面中
5. 页面—>跳到逻辑层—>新的页面
6. 就像TP一样
现在:
1. 我点击功能按钮,然后逻辑处理
2. 逻辑处理完,显示到页面
3. 这个整个过程是瀑布型的,像c一样从上到下的执行
4. 没有跳转
5. 页面—>走逻辑—>旧页面
6. 就像原生php一样


那么第二种为啥会把一个权限系统给废了?

1. 这的权限系统就是一个**逻辑层**
2. **入口**的是你要执行的**方法名**
3. **出口**是你要的**数据**
4. 这就一个**封装**
5. 不是废了,**我这个权限系统不是用来分层的**

假设你现在已经按不分层的想法写完了项目,那么有啥改进的地方?

1. 现在的情况,就是JFrame其实就是再次把UserReflectf封装了一次
2. 如果让增加功能,不仅权限里要增加相应的类,而且还要更改对应的页面
3. 做的这个权限系统不是让权限完全随用户更改添加,而是更重要的是**程序员做维护的时候好维护,自己用这个权限写项目思路更加清晰**
4. 如果是分层写的话,会这样思考
    1. 页面用不用改?哪里改?眼前全是html
    2. 逻辑用不用改?哪里改?眼前全是php逻辑
5. 如果不是分层写的话,会这样思考
    1. 乱死了,这个不是页面,跳过/这个不是逻辑,虽然明显,但是时间长了自己会**烦躁**。
6. **关键:**分层写,我可以在逻辑层处理多种任务,获得多次的数据,然后把**多组数据**都传递给新的页面
7. 分层于不分层的直观理解:
    1. 分层是把不分层的中写在**一个页面的所有过程**,集中放在一个新页面中,是一个**提取的关系**
    2. Ps:相似的功能可以通过将**各个过程封装到成方法中**
8. **分层知识为了更清晰,没其他作用**

我用反射实现的权限管理

1. 本质是实现了一对多的关系,**就是一个封装而已**
    1. 反射解决的是字符串产生对象的这个过程
    2. 一个字符串,就能对应那么多的具体的权限功能
2. 但是在这个项目中界面中只能是一对一的关系
    1. 一个界面已经绑定了一个代表权限的字符串了
3. 那这个权限系统在哪里才可以发挥它最大的作用
    1. 权限可以无限多
    2. 增加起来方便
    3. 一个权限功能的实现
        1. 权限名
        2. 方法名+参数
        3. 2这个是必须的,并不是我这个系统设计不合理留下的小尾巴
    4. 可以使用字符串直接对应权限,更加方便,直观(不用用数字去表示了)
    5. 总结:**因为封装好了,以后程序员用起来就比较方便了**
        1. 对用户来说,屁用没有
        2. 对程序员来说,却是极大的方便
    6. 这就是写框架要站的角度,和写项目占的角度**对比**
        1. 写框架是为让程序员更加爽、简单,框架就是一个封装,把麻烦的事情封装到一个黑盒中,然后,留一口,然程序员简单做到。在这过程中,程序员不用懂得其中的道理。
        2. 写项目时想的是如何实现一个功能,常常就为了一个boss功能较劲脑汁
        3. **但是**,顶尖的程序员,**做出项目来估计和设计框架是一样的**

关于接口再说两句

1. 我突然一下感受到了接口的好处
    1. 在一开始,我写底层一个最小单位的一个书籍管理权限,其实直接创建一个类,写方法,填注释就行了。这时写接口是一件麻烦的事情。
    2. 但是,当我从底层越来越向上封装,集成。这时,我如果想在其他类中说明也要使用这些方法,我只要加上对应的接口,方法自动出来,我填内容就行。一般来说,内容可以直接用底层的对象调用来实现
    3. 如果没有接口,我就得反复的回去看底层的具体的类实现。还生怕拉下一个。
    4. 最常用的地方是:当修改一个,增加一个功能时,接口可以方便的提醒你还有哪些地方要改。对你的操作有**指示作用**
    5. 有时候需要去写一个抽象类,但是,java本身的这个类中就有继承如JFrame继承自Frame,这时只能用接口来救急了。
    6. 用了接口,妈妈在也不用为我担心这个问题了。

我现在是站在一个写框架的角度去思考
但是,这也回答了一个问题我们看框架的时候会经历什么
最终,当你看完一个框架的时候,你脑子中是有一副类图的
like:


退书和总价的关系?

Index还是有问题
Table
1. 不初始化初始化
2. 覆盖


监听实现接口写在一起的好处

1. 可以何在一起写逻辑,而,单个是时候互不影响

如何能发挥出这个权限系统的最大能力

1. 数据库中用户的权限是一个**对应权限类名**的一个字符串
2. UserRflect的参数就是**权限字符串**
3. 然后就可以通过UserRflect来调用**对应权限类**下的方法
4. 能够去除当前权限下的所有方法,但是这些方法并不是简单地像输出不同的语句这样的**不需要任何输入,有统一输出的的方法**,所以不能做统一的界面去执行所有的功能。
5. 就像web中的导航栏,导航栏可以是统一的,但是导航栏连接到的每个单独不同的页面,**这得一个一个的做**。

如何让这个权限系统能够自动识别权限等级

1. 如果实现了能够自判等级大小
    1. 那么在做相关功能的时候只需要调用一个方法就可以实现
    2. 比如说我这里要实现**当前用户只能看权限比他低的人的信息**
    3. 这个函数的参数是**权限字符串1 权限字符串2**
    4. **返回boolean**
2. 另一种实现方式,在biz查询全部用户的时候,在这就判断出结果。
    1. 那么,这种实现方式就和权限系统没关系了
    2. 如果在biz中在声明userRflect。
        1. **userRflect是用来执行方法的,不是用来判断权限的!**
    3. 那么,可以再设计一个userRflect配套的类,参数和返回值像1所说的。
3. 现在方案定了,就差功能的实现了!
    1. 如何才算是权限大,有哪些情况
        1. 存在**包含关系**的时候能判断权限大小
            1. 第一种:一个role中包含了多个priority,那么这些priority都比这个role权限小
            2. 第二种:role1包含role2时,role1的权限大于role2 的权限。
            3. 循环嵌套
    2. 那么问题来了,如何**判断他们的包含关系**
        1. 组合关系的递归
4. **一下午!实现了**
5. 使用方式:
1
2
Compare compare = new Compare();		
boolean flag = compare.compare("priority.PriorityBaseBook", "role.RoleAll");

我这个系统里面priorit对于图书的和用用户的管理都分别设置的普通给用户client端和管理员manager端,这是不符合实际的

因为
1. 现实中,manager和client等级一高一低
2. 但是在priority包中定义的权限他们是没有高低的,都是一个独立的权限
怎么改:
1. 将priority包下的manager接口方法移动到role包下的对应的manager接口中,
2. 然后在role包下的实体类中重新实现接口中的方法
3. 对于role下包的实体类来说,是没有影响的
4. 因为,他implments 多个接口,我们现在只是把一个接口中的方法放到另一个接口中,对role实体类来说无视


循环包含的时候
1. 接口要自己去查基础功能的接口,可以直接从包含类implement中复制过来
2. 返回的方法和返回值类型也可直接拷贝,修改下长度


这个权限系统使用的要求:

1. 名字 属性名两部分  类名前准
    1. Priority包下,都是以priority为开头
    2. Role包下都已role开头
    3. 这个为了实现**权限等级判断的必要条件**
2. 方法 实例化哪几个方法
    1. PriorityExt抽象类下
        1. getReturns
        2. getMethods
    2. RoleExt extends PriorityExt
        1. getInclude
        2. **注意:role方法+1 judge**
3. 使用方式 
    1. 只需要一个**权限字符串**
4. 不同位置概念上的区别 
    1. Priotity包下,都是**最小单位的权限**,**并且不包含**,各为独立的个体
    2. Role包下,**可以不包括priority**,单独成为个体,作为最小单位。但是不同的是,**可以成为包含关系**

权限系统的完整包


最后要做的工作

1. 表格实现信息修改—完成
2. 管理员图书出库做判断—完成
3. 用户查无此id的
4. 退书应该加上专门的一个现实的表,然后进行点击退书
5. 注册用户的验证
6. 标题

如何学习table,如何了解table如何使用,他又那些功能。

1. 即使是别人写好的,你也不一定会用!
2. 手册的重要性,这个过程中一定会看大量的文档。
3. 文档的写法基本上也是按照一个个功能,**问题为主导的**。
4. 方便读者寻找自己想要的功能。
5. 这与我写文章的思路正好相符
6. 在这个过程中,有一个很大的问题就是**如何知道有什么功能**,以及会**涉及到其他**的什么类,接口,抽象等等
7. **如何实现其实不是最难的**,**手册**能解决这个问题,但是上面一个问题手册不一定能明显的解决。

table_3.addTableModelListener(this);
就像我不知Jtable中model这个东西一样


这回的文件封装的和屎一样

/*
* 这里应该是通过当前用户的权限去更改User.txt文件中的数据
* 但是我在client的接口中没有实现这个方法
* 现在不带了改了
* 所以就直接跳级
* 用底层封装好的文件操作改了
* 这样做是不对的!!!!!
*/


问题才是一个系统设计的核心!!!!

2017年7月1日 下午2:43


几个原则:
1. 测试类中方法不能太多
1. 解决方法:可以想象这个函数代表的功能是谁的
2. 测试类中主方法太长
1. 用c写的习惯了,100行以内根本不认为多
2. 而java中面向对象,超过二十行就觉得有点多了
3. 构造函数顺序:
1. 无参构造
2. 主键构造
3. 带所有参数的构造
4. 注释注释注释注释

各占1/3的时间

  1. 需求说明:
    1. 像关心下一代项目就没有需求说明,他想做出来再改
    2. mnt-lab也是这
    3. Report其实也是,那些流程都没有定出来写文档
  2. 动手之前的分析占了很长时间:report项目
  3. 完成之后的修改也要很长时间:mnt-lab门户项目

2017年6月30日 上午10:31

多线程的好处:
解决了多部分代码同时运行的情况
坏处:
每个线程占的资源少,单个线程的效率比较低

实现

1.     继承Thread
    1. run()运行代码
    2. start()开启线程:没有开始运行,进入了
    3. 一个线程类对象只能调用一次start()方法


图1 线程的四种状态(五种:创建,就绪,运行,阻塞,停止)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package package1;

public class ThreadTest extends Thread{
private String name;
//票的总数
private int num = 5;

public ThreadTest(String name){
this.name = name ;
}
public void run(){
while(num != 0){
System.out.println(name+"\t"+num--);
}
}
public static void main(String[] args) {
//创建了两个资源
ThreadTest tt = new ThreadTest("线程1");
ThreadTest tt1 = new ThreadTest("线程2");
//开启两个线程
tt.start();
tt1.start();
}
}

输出:

2. 接口Runnable     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package package1;

public class RunnableTest implements Runnable{
private String name ;
private int num =10;
public RunnableTest(String name) {
super();
this.name = name;
}

public static void main(String[] args) {
//创建资源
RunnableTest rt = new RunnableTest("线程1");
//同一个资源开两个线程
new Thread(rt).start();
new Thread(rt).start();
}

@Override
public void run() {
// TODO Auto-generated method stub
while(num != 0){
System.out.println(name+"\t"+num--);
}
}

}

输出1

输出2

对比 输出1 和 输出2 发现:

1. 输出1 一共输出的为11个,10输出了两次
2. 输出2 一共输出了10个,1-10各一次

总结:会有特殊情况出现

对比 程序1 和 程序2 发现:

1. 实现Runnable接口,方便实现资源共享
2. 继承Thread类受单继承影响,不适合多线程共享资源    
3. 只有Thread才能创建线程对象,Runnable只是实现run()方法
4. **多线程的特点是:**
            1. 一个对象的run()可以同时执行
            2. 多个对象的run()方法也可以同时执行


图2:Thread实现方式
Thread 的角色:提供资源 + 提供方法 + 运行方法

图3:Runnable实现方式
RunnableTest的角色:提供资源 + 提供方法run
Thread的角色:调用run

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package package1;

public class ThreadTest extends Thread{
private String name;
//票的总数
private int num = 5;

public ThreadTest(String name){
this.name = name ;
}
public void run(){
while(num != 0){
System.out.println(name+"\t"+num--);
}
}
public static void main(String[] args) {
ThreadTest tt = new ThreadTest("线程1");
//调用两次start,来模仿Thread和Runnable的关系
tt.start();
tt.start();
}
}

输出:

通过这个例子来理解Runnable 和 Thread的不同:

1. Thread的run方法不能让同一个对象同时调用进行,但,可以不同对象之间进行
2. Runnable 的run方法可以被同一个对象同时调用
3. 那么,在thread中,如果强制调用tt.start()两次,就类似于runnable调用两次start(),会发生什么呢?很可惜,因为start()方法是synchronized的(如图5),所以只能支持Runnable的两次(图7),不支持Thread的两次(图6)。


图5

图6

图7

Thread的方法:

  1. Static Thread currentThread()
  2. Final String getName()
  3. Final void setPriority();设置优先级
  4. Void start()
  5. Static void sleep() 让出执行权,不让出资源
  6. Final void yield()暂停当前线程,让其他线程(大家同一个起跑线) 礼让
  7. void run()执行线程

线程方法 + object方法

同步的前提:

  1. 两个或两个以上的线程
  2. 多个线程使用同一资源
    1. (必须用Runnable)或者 (Thread+ static资源变量)

同步的概念:

  1. 回去判断每个线程上的锁,浪费资源
  2. 同一个时间只能运行一个线程
  3. 同步可以解决线程中的安全问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package package1;

public class ThreadTest2 extends Thread {
private String name ;
public ThreadTest2(String name) {
this.name = name;
}
public static void main(String[] args) {
ThreadTest2 tt = new ThreadTest2("线程");
tt.start();
}
@Override
public void run(){
for(int i = 0 ;i < 4;i++){
System.out.println(i);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Thread.currentThread().yield();
}
}
}

以上代码使用
1. Thread.currentThread ().sleep 实现休眠
2. Thread.currentThread().yield() 实现礼让

同步代码块 同步方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package package1;

public class RunnableTest2 implements Runnable {
private int num = 10 ;
@Override
public void run() {
//这里要死循环 或者 循环判断
while(num != 0){
synchronized (RunnableTest2.class) {
if(num > 0){
System.out.println(Thread.currentThread().getName()+"\t"+num--);
}
}
// sail();//两种方法的交换
}

}
public synchronized void sail(){
if(num > 0){
System.out.println(Thread.currentThread().getName()+"\t"+num--);
}
}
public static void main(String[] args) {
RunnableTest2 rt = new RunnableTest2();
new Thread(rt).start();
new Thread(rt).start();
new Thread(rt).start();
new Thread(rt).start();

}
}

同步代码块 同步方法的区别:

1. 上面的代码包含的两种方法,通过注释来更换
2.  他们两种没有功能上的区别,可以相互替换
3. 写成方法可以更加灵活,因为可以分块写,不用写成一块了

同步的死锁:


理解:
1. 爸:我要你的成绩单
2. 儿:我要玩具
3. 爸:你先给我成绩单!
4. 儿:不!你先给我玩具!
5. 两个人怒目相对,谁都不给谁,就相互看着…….

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package package1;

public class LockRunnable implements Runnable {
private int flag = 1;
/*
* 这里是static,原因:
* 我在执行的时候初始化了两个LockRunnable对象,那么就会生成两套资源
* 也就是有 两个成绩单 和 两个玩具
* 所以,要通过static来保证只有 一个成绩单 和 一个玩具
*/
static Object o1 = new Object();//表示玩具
static Object o2 = new Object();//表示成绩单

@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(flag);//如果1 表示家长
if(flag == 1){
synchronized (o1) {//我现在要占用o1了,很幸运还在,我就把它拿走了,然后我接的执行
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (o2) {//我现在要占用o2了,但是发现不在有人拿着,我就等着!
System.out.println("可以给你玩具了");
}
}
}

if(flag == 0){
synchronized (o2) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (o1) {
System.out.println("可以给成绩单");
}
}
}
}

public static void main(String[] args) {

//这里是两个资源对象,所以要声明称static类型
LockRunnable t1 = new LockRunnable();
t1.flag =1 ;
LockRunnable t2 = new LockRunnable();
t2.flag =0 ;
new Thread(t1).start();
new Thread(t2).start();
}

}

在上面的代码中注意:

1. 正确理解synchronize的意思:
    1. **用在方法时,这个方法不能被同一个对象同时调用**
    2. 用在块时,()里的为需要站用的资源,根据这个进行判断
2.  为什么要使用static
3. 注意这里产生了两个LockRunnable资源对象,一个代表父亲,一个代表儿子

2017年6月30日 下午5:09

1
2
3
4
5
6
7
8
9
10
11
//馒头类
class SteamBread{
int id;//馒头编号

SteamBread(int id){
this.id = id;
}
public String toString(){
return "steamBread:"+id;
}
}
1
//装馒头的框,栈结构
class SyncStack{
	int index = 0;
	SteamBread[] stb = new SteamBread[6];//构造馒头数组,相当于馒头筐,容量是6
	
	//放入框中,相当于入栈
	public synchronized void push(SteamBread sb){
		while(index==stb.length){//筐满了,即栈满,
			try {
				this.wait();//让当前线程等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.notify();//唤醒在此对象监视器上等待的单个线程,即消费者线程
		stb[index] = sb;
		this.index++;
	}
	
	//从框中拿出,相当于出栈
	public synchronized SteamBread pop(){
		while(index==0){//筐空了,即栈空
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.notify();
		this.index--;//push第n个之后,this.index++,使栈顶为n+1,故return之前要减一
		return stb[index];
	}
}
1
//生产者类,实现了Runnable接口,以便于构造生产者线程
class Producer implements Runnable{
	SyncStack ss = null;
	Producer(SyncStack ss){
		this.ss = ss;
	}
	@Override
	public void run() {
		// 开始生产馒头
		for(int i=0;i<20;i++){
			SteamBread stb = new SteamBread(i);
			ss.push(stb);
			System.out.println("生产了"+stb);
			try {
				Thread.sleep(10);//每生产一个馒头,睡觉10毫秒
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
1
//消费者类,实现了Runnable接口,以便于构造消费者线程
class Consume implements Runnable{
	SyncStack ss = null;
	public Consume(SyncStack ss) {
		super();
		this.ss = ss;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<20;i++){//开始消费馒头
			SteamBread stb = ss.pop();
			System.out.println("消费了"+stb);
			try {
				Thread.sleep(100);//每消费一个馒头,睡觉100毫秒。即生产多个,消费一个
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}	
}
1
2
package thread;
//主类
public class ProduceConsume {
	public static void main(String[] args) {
		SyncStack ss = new SyncStack();//建造一个装馒头的框
		Producer p = new Producer(ss);//新建一个生产者,使之持有框
		Consume c = new Consume(ss);//新建一个消费者,使之持有同一个框
		Thread tp = new Thread(p);//新建一个生产者线程
		Thread tc = new Thread(c);//新建一个消费者线程
		tp.start();//启动生产者线程
tc.start();//启动消费者线程 } }

如何思考:

1. 根据我对算法的总结“先生活例子,然后提取概括,后算法实现”的思路
2. 我们先想这几个问题:
    1. 堆栈空了怎么办:消费者停止消费
    2. 堆栈满了怎么办:生产者停止生产
    3. 那么:我们既然要实现停止的功能,这时就得想到**线程**
    4. 因为:如果不用runnable,那么方法执行其不可控,只能一次执行完,或者出错一般的改写
3. 如果生产和消费同时进行怎么办:**sync**
4. 如何保证只用一个栈呢:**引用传参**
5. 容易忽略的问题:
    1. 既然生产和消费者都能停止,那么何时唤醒呢?
    2. 这个问题关键不是如何解决,而是注意到这个问题。
    3. **结论:等待 唤醒成对出现**
    4. **生产的时候唤醒消费,消费的时候唤醒生产**
    5. **生产暂停生产 ,消费暂停生产**
6. 实现:
    1. 先馒头 -> 栈 -> 生产者 -> 消费者 -> 总类

总结讨论:

1. 在这个实下中**一个消费者 ,一个生产者 ,一个栈**
2. 如果同一个消费者执行多次start(),同一个消费者也执行多次start()呢(图1)?


图1
这时还是共享一个资源
3. 如果多个消费者,多个生产者呢(图2)并且在生产者消费中将栈都声明为static

图2
这时还是共享一个资源
4. 如果多个消费者,多个生产者呢,但是却不将生产者消费中将栈声明为static
1. 这时,两个消费者使用不同的资源,两个消费也使用不同的资源
2. 但是,由于是引用类型,本质内存中只有一个资源空间
3. 程序就乱了!!!!!

注:在多线程中,虽然我们使用了多线程,但是其实一个具体的时刻还是只有一个线程运行,变得只是这些各自的多线程会走走停停,交互运行

2017年6月29日 下午12:09

第一个部分:数组定义

定义的方法:
1. int num[]= new int[10];必须说明长度 int初始化为0
2. int []num;
num=new int[5]
3. int num[]=new int[]{1,2,3,4}
4. int num[]={1,2,4,5,6,7}
String string1[] = {“1”,”2”};

长度方面的错:
1. String str[] = new String[];不能不说明长度
2. int num[10];编译出错 说明长度情况下,必须要有指向的内容
3. String str[] = new String[3]{“1”,”2”,”3”}; 编译错

先定义后初始化(一对三错):
String str[];
str = new String[]{“1”,”2”};对

String str[];
str[] = new String[]{"1","2"};错

String str[];
str = {"1","2"};错

int str[];
str ={1,2};错

总结:分开只能按1写

第二部分:局部 and 全局 基本类型 and 对象类型 数组 and 非数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package package1;

public class Test5 {
static Student student[] = new Student[10];
static String string[] = new String[10];
static Student student2[] = new Student[]{new Student(),new Student()};
static String string2[] = new String[]{new String(),new String()};
public static void main(String[] args) {

System.out.println(student.length);//new Student[10] 定义长度
System.out.println(student[0]);//但是没在堆中定义空间

System.out.println(string.length);
System.out.println(string[0]);//但是没在堆中定义空间

System.out.println(student2.length);
System.out.println(student2[0]);

System.out.println(string2.length);
System.out.println(string2[0]);//这里输出的是""打印出来为空
}

}

输出:

1
2
3
4
5
6
7
10
null
10
null
2
package1.Student@677327b6
2

[“”]自己加的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package package1;

public class Test4 {

public static void main(String[] args) {

Student student[] = new Student[10];
System.out.println(student.length);//new Student[10] 定义长度
System.out.println(student[0]);//但是没在堆中定义空间

String string[] = new String[10];
System.out.println(string.length);
System.out.println(string[0]);//但是没在堆中定义空间

Student student2[] = new Student[]{new Student(),new Student()};
System.out.println(student2.length);
System.out.println(student2[0]);

String string2[] = new String[]{new String(),new String()};
System.out.println(string2.length);
System.out.println(string2[0]);//这里输出的是""打印出来为空

//Student String 数组是完全一样的
//除了new的默认值以外,String的默认值是"" Student默认值为包名+地址

}

}

输出:

1
2
3
4
5
6
7
8
10
null
10
null
2
package1.Student@677327b6
2
[“”]自己加的

发现:
1. 他们的输出是一样的,说明局部变量的概念对数组无效


图1:对象声明为局部

图2:对象声明为全局
发现:
1. 对于非数组对象(单个对象),局部变量概念有效

加入基本类型后:
1. 局部中:
基本类型 对象类型
数组(只定义长度) 0 null
非数组(只对象声明) 错 错
3. 全局中:
基本类型 对象类型
数组(只定义长度) 0 null
非数组(只对象声明) 0 null

总结:
对于数组类型: 基本类型是0,对象类型为null
对于非数组:局部中一定错 ,全局中 0 和 null

String 和 Student的区别(单个对象+以初始化)

1. String的默认值是"" ,Student默认值为包名+地址
2. 其他的,Student String 数组是完全一样的 

注:注意前提为以初始化

2017年6月29日 上午10:12

java的命名首字符

可以是字符,下划线,$。不可以是关键字,保留字,%等。

1
int $1= 1;
int _1 = 1;

类型转换

正文内容:类型转换向大的类型转换。
Eg:
1. char sex = 12 + ‘c’;1.7不会报错,但是不能超过char的容量和ASSIC
2. int age = 20 ;
char sex = age + ‘c’;编译错
3. int age = (int)1234566789;运行报错out of range 不能抢转
4. boolean yes = 1 ; 编译错

进制

1. int n2 = 01;八进制
2. int n3 = 0x1;16进制 

switch注意事项

1. dk1.6中switch不可以字符串 ,double 都不支持
2. 条件-确定区间的开头,break决定退出
3. 不要少break
4. 于if的不同:
    1. switch 可以转换成if ,但是if不一定能转成switch
    2. switch 有类型的限制,而if没有

for 和 switch连用,break和continue的特点

  1. break不是跳出for而是跳出switch
  2. continue和if中的一样,后面的不执行了
    1
    for(int i =  0; i <= 10 ;i++){
    	switch(2){
    		case 1:
    			System.out.println("1");
    			continue;
    		case 2:
    			System.out.println("2");
    			break;
    	}
    	System.out.println("3");
    }

null

null是没有空间的

1. 包名小写 
2. 不以.为开头 
3. ;结尾

String 的使用

1. java.lang包
2. String str = "";长度为0
3. String str = " ";长度为1
4. 中文的长度也是1
5. String类是final类
6. == 内存池
7. equals()的比较原理 一个个字符的比较
8. == 比较是不是相同的地址,内存的首地址

大小写转换
1
toLowerCase()
toUpperCase()
equalsIgnoreCase()
A.concat(B);B接到A后面
1
String s1 = "你好";
String name ="张三";
String sen = s1.concat(name);
;不影响s1的值
s1 = s1.concat(name);
;这个才影响s1的值
提取下标1:返回第一个匹配的位置
1
indexOf(int ch);
indexOf(String value);

lastIndexOf(int ch)
lastIndexOf(String value)
提取内容2:返回字符串的一部分
1
subString(int index);
subString(int index_start,int index_end);包括start不包括end
tirm();清楚前后空格,重新产生对象,不影响本身
替换
1
str.replace()->替换replace
转换类型:
1
str.valueOf() -> 字符串 -> toCharArray()
			           -> charAt()

存储

1. 栈区:编译器自动分配释放
        1. 存放函数值+局部变量
2. 堆:有程序员new分配释放,若程序员不释放,程序结束时os释放     
3. 全局区:全局变量+静态变量  放在堆中初始化之后放在一起,没有初始化的放在旁边的一块区域。程序结束后系统释放
4. 文字常量区:“abc”
5. 程序代码区:

重载

1
public void print(int a , String b);
public void print(String a,int b);//是重载

public void print(int a ,int b);
public void print(int b,int a);//不是重载
重载的条件
1. 同一个类中
2. 参数列表不同
3. 方法名相同
4. 和返回值,访问修饰符无关

override 重写
overlad 重载

static

static 三种用途
1. 属性 在堆中 位置:全局变量+static方法中
2. 静态块 常用去提前配置 (可以把括号理解成一种简写方式)

1
static {
	syso("*************")
}
3. 方法

不能直接访问非静态的方法+成员变量(也不能定义)
可以通过 对象. 去访问

1
public void play(){
	static int local = 5;    错
}

char于int的关系

举例:

1
char x= 100;	
System.out.println(x);//d
System.out.println(x + 0);//100

int y = 100;
System.out.println(y);//100
System.out.println((char)y);//d
输出:
    d
    100
    100
    d

总结:他们内存本质是一样的,唯二的区别是
        1. int是16位,char是8位
        2. 输出时的方式不同