Java学习笔记

错题

  • 通过super可调用父类构造函数。对

  • 构造函数的返回类型只能是void型。错,造函数必须与类名相同且没有返回值,void是空返回值并不是无返回值所以是错的

  • 一般在创建新对象时,系统会自动调用构造函数。对

  • 【单选题】下面赋值语句不合法的是___D___。

    A.Long a=(Long)(long)3;

    B.Long b=3L;

    C. Long c=Long.parseLong(“3”);

    D.Long d=(Long )3;

    下面赋值语句不合法的是 D。在Java中,”Long”类是原始数据类型long的包装类,必须使用关键字”new”来构造包装类,才能创建有效的对象。从长值3创建Long对象的正确方法如下:

    1
    Long d = new Long(3);

    其他三个选项都是从长值3创建Long对象的有效方法。选项A从将整数值3转换为长值,然后再转换为Long的结果创建Long对象。选项B从长值3L创建Long对象。选项C通过调用Long类的parseLong方法,从字符串值”3”创建Long对象。

  • 【单选题】java语言中,在定义类时不能使用的修饰符是____B______。 A.public B.private C.abstract D.final

    • 答案是 B:private。在 Java 语言中,private 是一种访问修饰符,它只能在类的内部使用,不能用于定义类。
    • A 选项:public。public 是一种访问修饰符,它表示在任何地方都能够访问。定义类时可以使用 public 修饰符。
    • C 选项:abstract。abstract 是一种修饰符,它用于定义抽象类和抽象方法。定义类时可以使用 abstract 修饰符。
    • D 选项:final。final 是一种修饰符,它表示类不能被继承,方法不能被重写。定义类时可以使用 final 修饰符。
  • 在Java语言中,下面关于Math类的常见操作不正确的描述是_____D__B___。

A.执行Math.ceil(-10.5) 语句的结果是-10.0

B.执行Math.round(10.5) 语句的结果是11.0 (答案是11,没有小数点!)

C.执行Math.floor(-10.5) 语句的结果是-11.0

D.执行Math.round(-10.5) 语句的结果是-10

Math.round 不完全是四舍五入,当刚好为负数且刚好0.5,舍入到正无穷方向上的整数。如Math.round(-20.5)结果为-20

  • 【单选题】执行下面程序段后,输出结果是___C______。

    int a=4,b=6,c=8;

    System.out.println(++a*b—c );

    A.11

    B.16

    C.22

    D.23

    注意后面的—前两个应该是b的后缀,而不是后两个是c的前缀,由于是后缀,无影响,而a的前缀加了用,因此是5×6-8结果为22,而且b的值变为5,a为5。

  • 下面概念中,不属于面向对象程序设计的是_____A_____。

    A.过程调用

    B.对象

    C.类

    D.继承

    文本框获得焦点时回车和按钮单击,都属于ActionEvent事件,也就是说文本框和按钮可以作为ActionEvent事件的事件源。

    选择框的选中,和下拉列表的选中,都是在触发ItemEvent事件。

    不同类型的事件的监听器要实现的接口不同,对于ActionEvent事件,这个接口是 ActionListener,实现其中的actionPerformed方法,方法传入的也是ActionEvent对象。

    而对于ItemEvent事件,则要实现的是ItemListener接口,实现其中的itemStateChanged方法,方法传入的是ItemEvent对象。

  • 下面关于java.sql包中接口和类的描述不正确的是__B__。

    A.Connection 接口:表示数据库连接

    B.DriverManager类:表示驱动器 (错,JDBC 的管理层,作用于用户和驱动程序之间。)

    C.ResultSet接口:表示SQL查询语句返回的结果集

    D.Statement接口:负责执行SQL语句

  • 在Java语言中,下面用于执行存储过程SQL语句的是___B___。

    A.Statement

    B.CallableStatement

    C.createStatement

    D.PreparedStatement

    Statement 对象有三种:(Statement)对象用于执行不带参数的简单 SQL语句; (PreparedStatement)对象继承Statement,用于执行带或不带参数的预编译 SOL语句:(CallableStatement) 对象继承PreparedStatement,用于执行对数据库存储过程的调用

  • 在Java语言中,下面关于Scanner类描述错误的是_____D__C___。

    A.Scanner类可以方便的完成输入流的输入操作

    B.Scanner sc=new Scanner(System.in);//从标准输入中扫描

    C.Scanner类位于javax.util包中,使用时需要import导入**(是java.util)**

    D.Scanner可以扫描指定的文件.

    D是对的,C是错的。

  • 在Java语言中,下面不属于ComponentEvent的子类是____D______。

    A.InputEvent

    B.FocusEvent

    C.WindowEvent

    D.ItemEvent

    ItemEvent不属于ComponentEvent的子类。

  • 在Java语言中,下面关于组件定义错误的是____ D_____。

    A.TextField tf=new TextField(3);//int型指定列宽

    B.Timer tr=new Timer();

    C.JFileChooser jf=new JFileChooser();

    D.TextArea ta=new TextArea(3);

  • 在Java语言中,以下____C____项是接口B的正确定义。

    A.interface B{ void print(){ };}

    B.abstract interface B{void print(){}}

    C.interface B {void print();}

    D.interface B extend A { void print(){}}//A为已定义接口

    1
    2
    3
    4
    5
    public interface Paintable{
    void draw();//没有{},可省略public abstract关键字
    }
    interface intf1{}
    interface intf2 extends intf1{}//接口继承接口
  • 下面概念中,不属于面向对象程序设计的是_____A_____。

    A.过程调用

    B.对象

    C.类

    D.继承

  • 在Java语言中,成员变量中被static关键字修饰的变量,叫 B

    A.变量

    B.类变量 (静态变量的成员变量)

    C.实例变量

    D.整型变量

  • 在Java语言中,下面关于String类的常见操作不正确的描述是______B____。

    A.假设 s ="class";则执行char c = s.charAt(1)语句后变量c的值是l

    B.indexOf方法是查找特定字符或字符串在当前字符串中的起始位置,如果不存在则返回0 。(返回-1)

    C.concat方法的作用是进行字符串的连接,将两个字符串连接以后形成一个新的字符串

    D.equals方法的作用是判断两个字符串对象的内容是否相同

    charAt() 方法用于返回指定索引处的字符。索引范围为从 0length() - 1

  • 下面关于try catch语句中异常类排列顺序正确的说法是____B______。

    A.父类异常在前,子类异常在后

    B.父类异常在后,子类异常在前

    C.父类和子类异常排列顺序前后无影响

    D.只能有子类异常

  • 已知Integer.MAX_VALUE 的值为2147483647,在执行“Integer max1=Integer.MAX_VALUE,max2= max1+1;”语句后,max2等于____A______。

    A.-2147483648

    B.2147483647

    C.0

    D.2147483648

  • 在Java语言中,Person类有一个成员变量age被protected修饰,下面关于age说法不正确的是___C_______。

    A.能被Person的子类访问。

    B.能被Person类所在同一个包中的其它类访问。

    C.能被Person类所在包之外的其它类访问。

    D.不能被Person类所在包之外的其它类访问。

    protected 访问控制符能被用于方法和成员变量
    声明为protected的方法和成员变量能被同一个包里的所有类所访问,就像默认修饰符package一样
    能被该类的子类所访问,子类可以和父类不在一个包中。
    另一个包中的子类只能通过子类或其子类的引用来访问父类中受保护的成员。同一包中的子类没有此限制。这样可以确保来自其他包的类只访问属于其继承层次结构一部分的成员

  • 下面哪项不属于Statement接口提供的3个执行SQL语句的方法______A____。

    A.executeDelete(String sql)

    B.executeUpdate(String sql)

    C.executeQuery(String sql)

    D.execute(String sql)

  • 在Java语言中,下面关于RandomAccessFile描述错误的是_____D_____。

    A.实现DataInput和DataOutput接口

    B.getFilePointer()方法:返回此文件中的当前偏移量

    C.readFloat()方法:从此文件读取一个 float

    D.writeChar(int v):按双字节值将char写入该文件,先写低字节(将一个字符作为一个两个字节的值写入基础输出流,其中高字节在前。)

  • 在Java语言中,下面相关描述错误的是____B______。

    A.File类对象对应于系统中的一个目录或文件

    B.CharArrayReader 是一个把字符数组作为源的输出流的实现.(输入流)

    C.FileInputStream:以字节流方式读取

    D.FileReader:把文件转换为字符流读入

  • 下面关于Java事件描述错误的是_____A_____。

    A.只有外部操作会产生事件

    B.可以通过继承EventObject类编写自定义事件类

    C.事件处理的三要素包括事件源、事件以及事件监听器

    D.要在事件源上注册事件监听器

  • 在Java语言中,下面关于Applet描述错误的是A。

    A.Applet能执行任何本地计算机上的程序(错误)

    B.Applet的生命周期中有四个状态:初始态、运行态、停止态和消亡态

    C.Applet的init()方法在Applet的生存周期中只调用一次

    D.Applet应用程序必须嵌入在HTML页面中,才能得到解释执行

    此外,Applet是Java类,且通常情况下不能进行文件的I/O操作

  • 在Java语言中,下面关于List不正确的描述是_______B___。

    A.List是在java.util包中

    B.List是一个类(是接口)

    C.List具有get(int index)方法

    D.List是一个接口

    List,Set,Map都是接口

  • 在Java语言中,类Double定义在以下的哪个包中_____C_____。

    A.java.io

    B.javax.lang

    C.java.lang

    D.java.util

  • 在Java语言中,下面关于接口错误描述的是___A___。

    A.接口不仅包括方法的特征,还有方法的实现。

    B.接口只允许public 和abstract修饰。

    C.接口中的属性只能被public 、final、static修饰。

    D.一个类可以实现多个接口。

    接口中的属性只能被public 、final、static修饰,而且必须赋值,因为是常量,在后面不能改变,否则会报错,不会给默认值的

  • 在Java语言中,执行下列程序段后,i的结果是____D______。

    int i;

    for(i=0;i<10;i++)

    {

    if (i>4){ continue;}// 一直跳过

    if(i>7 ){ System.out.println(i); break;}

    }

    A.6

    B.8

    C.9

    D.10

  • 在Java语言中,下面关于颜色定义不合法的是___B_______。

    A.Color c1=new Color(0xffffff)

    B.Color c2=new Color(Color.BLUE)

    C.Color c3=new Color(0,0,255)

    D.Color c4=new Color(0.2f,0.6f,1.0f)

    Color 是 Java 中的一个类,可以用来表示颜色。在 Java 中,可以使用以下几种方法来定义颜色:

    • 使用 16 进制 RGB 值来定义颜色,例如 A.Color c1=new Color(0xffffff)。
    • 使用 24 位 RGB 值来定义颜色,例如 C.Color c3=new Color(0,0,255)。
    • 使用浮点型 RGB 值来定义颜色,例如 D.Color c4=new Color(0.2f,0.6f,1.0f)。

    B.Color c2=new Color(Color.BLUE) 中的 Color.BLUE 是预定义的颜色常量,它表示蓝色。这种方式并不能用来定义颜色,因此 B 选项是不合法的。

  • 下面不属于Java语言中常见事件类型的是____C______。

    A.KeyEvent

    B.MouseEvent

    C.TouchEvent

    D.ItemEvent

  • 在Java语言中,下面不符合数组定义格式的是___D_______。

    A.int []a=new int [3];

    B.int b[]={1,2,3};

    C.int c[]=new int [3];

    D.int e[3]=new int [3];

    在 Java 中,数组是用于存储一组相同类型的数据的数据结构。在 Java 中,可以使用以下几种方法来定义数组:

    • 使用 new 运算符来定义数组,例如 A.int []a=new int [3]; 和 C.int c[]=new int [3];。
    • 使用 {} 来定义数组并初始化数组元素,例如 B.int b[]={1,2,3};。
  • 在Java语言中,下面变量命名不合法的有 C

    A.$fn

    B.p5p

    C.static

    D._user

    必须以字母、下划线、或者美元符$开头;

  • 在Java语言中,下面不属于JDBC的主要功能是A

    A.解析SQL语句

    B.处理数据库的返回结果

    C.建立与数据库或者其他数据源的连接

    D.向数据库发送SQL命令

  • 在Java语言中,下面用于执行简单的不带参数的SQL语句是 A

    A.Statement

    B.PreparedStatement

    C.CallableStatement

    D.createStatement

  • 定义int A=5,执行“System.out.println(“a=”+((A<5)?5.1:4));” 语句的结果是 B

    A.a=5.1

    B.a=4.0

    C.a=5

    D.a=4

    三目运算符后面类型不同需要类型升级

  • 在Java语言中,下面关于包描述不正确的是 C

    A.包提供了访问权限和命名的管理机制

    B.包是Java提供的一种区别类的名字空间的机制

    C.类只能访问其所在包中的所有类

    D.包是类的组织方式,是一组相关类和接口的集合

    (1)具有public权限的类能被所有包中的类访问,与所在的包无关(2)具有缺省权限的类只能被所在包中的类访问,不能再其包外访问

  • 定义char x=’a’,下面赋值语句不合法的有 B

    A.float b=x;

    B.byte c=x;

    C.double d=x;

    D.int a=x;

    应该为 byte c=(byte)x;

  • 在Java语言中,下面关于异常的错误描述是_____D_____。

    A.异常是java提供的用于处理程序中错误的一种机制

    B.java.lang. Exception类是所有异常的父类

    C.java.lang.NullPointerException是空指针异常类

    D.当异常产生时,程序会自动跳转到异常处理程序

  • 在Java语言中,下面关于File类描述错误的是 A

    A.执行File f=new File(“e:\txx.txt”)语句的结果是在e盘上创建了一个txx.txt文件

    B.File类对象对应于系统中的一个目录和文件

    C.File类对象描述文件名、可否读写等属性,但不读写文件

    D.一旦创建,File对象表示的抽象路径名将不会改变

    File f=new File;创建的是一个对象

  • 在Java语言中,下面关于AWT组件描述错误的是 C

    A.Choice:制作用于单选的下拉列表

    B.与菜单相关的类主要有三个: MenuBar、Menu、Menultem

    C.Panel类可作为容器容纳其它组件,也可以独立存在

    D.Canvas:代表屏幕上一块空白的矩形区域

    Panel类可作为容器容纳其它组件,也可以独立存在必须在窗体容器中使用,无法脱离窗体显示

  • substring()截取字符串,从索引0开始计数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    String Str = new String("This is text");

    System.out.print("返回值 :" );
    System.out.println(Str.substring(4) );//4开始到最后

    System.out.print("返回值 :" );
    System.out.println(Str.substring(4, 10) );//含头不含尾
    //结果
    返回值 : is text
    返回值 : is te

    public int index0f(int ch,int fromlndex): 返回从 fromlndex 位置开始查找指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。

  • 编译JavaApplication源程序文件将产生相应的字节码文件,这些字节码文件的扩展名为D

    A. .html

    B. .Java

    C. .exe

    D. .class

  • 字符串比较。Java中,使用”==”比较字符串时,判断的是两个字符串是否存放在相同的位置。

    1
    2
    3
    4
    5
    6
    7
    8
    例如:x = "Hello"; y = "Hello";

    x == y; //就是True

    x == "Hello"; //也是True

    如果 String z=new String(x);
    x==z;//结果就是false
  • 在Java语言中,关于final修饰符的说法不正确的是 C

    A.fnal成员变量表示常量,只能被赋值一次,赋值后值不再改变

    B.final类不能被继承,没有子类,fnal类中的方法默认是final的

    C.final能用于修饰构造方法

    D.final方法不能被子类的方法覆盖,但可以被继承

    final修饰的类,为最终类,该类不能被继承。如String 类;final修饰的方法可以被继承和重载,但不能被重写、覆盖;final修饰的变量不能被修改,是个常量

  • main是static,所以不能直接调用非静态的方法

  • 方法重载就是一个类中有多个同名但有不同形参和方法体的方法 对

  • Java String类 trim() 方法用于删除字符串的头尾空白符

大题模板

集合类

ArrayList

1
2
3
4
5
6
7
8
9
10
11
12
13
List<String> list=new ArrayList<>();
list.add("a");
//第一种
Iterator<String> iterator=list.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
iterator.remove();//移除
}
//第二种
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
list.remove(i)//移除
}

TreeSet

截取部分输出 it=tree.SubSet(头,尾).iterator(); it=tree.headSet(边界).iterator();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
TreeSet<UpdateStu>tree =new TreeSet<>();
UpdateStu stu1=new UpdateStu("李同学", 01011);
UpdateStu stu2=new UpdateStu("陈同学",01021);
...;
tree.add(stu1);
tree.add(stu2);
tree.add(stu3);
tree.add(stu4);
Iterator<UpdateStu> it=tree.iterator();
it=tree.headSet(stu2).iterator();
System.out.println("截取前面部分的集合:");
while(it.hasNext()){
System.out.println(it.next());
}
it=tree.subSet(stu2, stu3).iterator();
System.out.println("截取中间部分的集合");
while(it.hasNext()){
System.out.println(it.next());
}

文件存取

字节流创建–输入–读取

1
File file=new File("MyFile.txt");

写入文件:

1
2
3
4
FileOutputStream outputStream=new FileOutputStream(file);//创建输出流
byte buy[]="Java程序设计".getBytes();//准备字节数组
outputStream.write(buy);//写入
outputStream.close();

FileOutputStream里放file,准备byte[]是字符串.getBytes(),写入用write

读取

1
2
3
4
FileInputStream inputStream=new FileInputStream(file);//准备输入流
byte byt[]=new byte[1024];//准备字节数组接收
int len=inputStream.read(byt);//read读取
inputStream.close();

读取用read,返回值是长度

文件信息

1
2
3
4
System.out.println("文件长度:"+file.length()+"字节");
System.out.println("文件路径:"+file.getAbsolutePath());
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
System.out.println("修改时间"+simpleDateFormat.format(file.lastModified()));

(缓存)字符流

写入

1
2
3
4
5
FileWriter fw=new FileWriter(file);
BufferedWriter bw=new BufferedWriter(fw);
String str="你好";
bw.write(str);//写入字符串
bw.newLine();//写入换行符

FileWriter里放file,Buffered里放FW,写入用write,换行符newLine()

读取

1
2
3
4
5
6
FileReader fr=new FileReader(file);
BufferedReader br=new BufferedReader(fr);
String tmp=null;//缓存临时字符串
while ((tmp=br.readLine())!=null) {
System.out.println(tmp);//输出每一行
}

FileReader里放file,BufferedReader里放FR,读取每一行用br.readLine(),字符串存

Swing

事件监听

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
class MyJDialog extends JDialog{
public MyJDialog(MyFrame frame) {//参数是MyFrame
super(frame,"这是一个对话框",flase);//窗体、标题、是否阻塞
Container container=getContentPane();//获取主窗体
container.add(new JLabel("这是一个对话框"));//在容器中添加标签
setBounds(120,120,100,100);//设置对话框在桌面显示的坐标和大小
}
}
public class MyFrame extends JFrame{
public MyFrame() {
Container container=getContentPane();//获取窗体主容器

bl.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new MyJDialog(MyFrame.this).setVisible(true);
}
});
container.add(bl);
setVisible(true);
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
new MyFrame();
}

}

组件.addAcitonListener里边new Actionlistener

多线程

消费生产栈

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
60
61
62
63
64
class ms{
private Thread consume;
private Thread product;
private Random random=new Random();
private int count=0;
private Stack<Integer> st=new Stack<Integer>();
public ms() {

product=new Thread(new Runnable() {
public void run() {
while(true) {
try
{
if(count<10)
{
int s=random.nextInt(1000);
st.push(s);
count++;
System.out.println("生产数据"+s);
Thread.sleep(1);
}
else
Thread.sleep(100);
}
catch (Exception e) {
// TODO: handle exception
}
}
}});
product.start();

consume=new Thread(new Runnable() {
public void run() {
while(true) {
try
{
if(count>0)
{
int s=st.pop();
count--;
System.out.println("消费数据:"+s);
Thread.sleep(1);
}
else
Thread.sleep(100);
}
catch (Exception e) {
// TODO: handle exception
}
}
}});
consume.start();
}
}

public class Lab14_4{

public static void main(String[] args) {
// TODO 自动生成的方法存根
new ms();
}

}

网络通信

TCP 双向通信

服务器端

接收用BufferReader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MySever{
public ServerSocket sever;//创建热线
public Socket socket;//客服
private PrintWriter writer;//输出流
void start(){
//绑定端口
server=new ServerSocket(8998);
while(true){
socket=server.accept();//等待接收,阻塞
//套接字客服传信息,用缓存输入流,里边是InputStreamReader,再里边是socket.getInputStream
BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream));
writer=new PrintWriter(socket.getOutputStream,true);
while(true){//又一个
//字符串接数据 readerLine()
String message=reader.readerLine();
wrter.println("收到,谢谢你!");
}
reader.close();//先关流
socket.close();
}
}
}

socket.getInputStream就是文件存取的file,类比一下,而InputStreamReader就是FileReader

客户端

发出用PrintWriter

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
class MyClient{
Socket socket;//套接字
private PrintWriter writer;//写数据,用PrintWriter而不是Buffer
private BufferedReader reader;//输入流,读取
public MyClient(){
//一些修饰,如swing之类的

//传数据
String message="hello";//准备数据
writer.println(message);//传输即可
}
//连接另外写一个函数
public void connect(){
socket=new Socket("127.0.0.1",8998);//绑定ip和端口
writer=new PrintWriter(socket.getOutputStream(),true);//创建流,一层就够了
//接收信息
reader=new BufferedReader(new InputStreamReader(socket.getInpuString));
//准备线程
readThread=new Thread(new Runnable() {

@Override
public void run() {
String message=reader.reaLine();
syso(message);
}
}
}
}

connect里:绑定端口指定ip,准备流

构造里:writer.println传数据

UDP

服务器端
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
//指定地址
//实例化,绑定端口
//加入组
//构造里配置,run里边接收/发送
//准备DataPackage,字符转字节数组,发送socket.send()
class Notification extends Thread{
int port;//端口
InetAddress iAddress=null;//ip
MulticastSocket socket=null;//MulticastSocket socket
String weather=new String();//发送的信息
public Receive() {
//配置
//指定地址
try {
port=9898;
iAddress=InetAddress.getByName("224.255.10.0");//getByName返回的InetAddress
MulticastSocket socket=new MulticastSocket(port);
socket.setTimeToLive(1);
socket.joinGroup(iAddress);
} catch (UnknownHostException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
@Override
public void run() {
// 发送信息
//准备
DatagramPacket packet=null;
byte data[]=weather.getBytes();//字符转字节数组
packet=new DatagramPacket(data, data.length, iAddress, port);
socket.send(packet);
sleep(100);
}
}

构造里边配置:getName获取指定地址,实例化MulticastSocket绑定端口,socket加入组

run()里边:准备包,字符转字节数组,实例化DatagramPacket,socket.send(packet)

客户机端
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
class Receive implements Runnable{
int port;//端口
InetAddress group=null;//ip
MulticastSocket socket=null;//MulticastSocket socket

public Receive() {
//配置
//指定地址
try {
port=9898;
group=InetAddress.getByName("224.255.10.0");//getByName返回的InetAddress
MulticastSocket socket=new MulticastSocket(port);
socket.joinGroup(group);
} catch (UnknownHostException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
@Override
public void run() {
byte bty[]=new byte[1024];
DatagramPacket packet=new DatagramPacket(bty, bty.length, group, port);
try {
socket.receive(packet);
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
String message=new String(packet.getData(), 0, packet.getLength());
System.out.println(message);
}
}

构造里边:构造里边配置:getName获取指定地址,实例化MulticastSocket绑定端口,socket加入组

run()里边:准备字节数组,实例化DatagramPacket,socket.receive(packet),然后new String(packet.getData(), packet.getLength())packet转字符

main函数当作类外的函数来看待

1、包内访问权限和protected有何不同?(p98)

包内访问权限介于private和protected之间

包外的派生类,可以在派生类内部调用基类的protected成员

包外的派生类,无法在派生类内部调用基类的包内访问成员

  • 当两个包在同一个项目之中时,可以通过完整类目继承包外的基类访问
1
2
3
4
package Lab2;
class newClass extends Lab1.myClass{//包名.类名

}
  • 当两个包在不同的项目之中时,将基类项目导出为.jar,将该.jar文件复制到派生类所在的项目文件夹中,然后右键点击选择”add building path”添加到构建路径,然后代码与上面相同。

2、所有数组都能排序吗?

错误。排序要有一定依据,没有指定依据无法排序。如类型为类的数组,可比较的属性不止一种(implements比较器)。

3、接口

接口的本质是标准,是设计者、实现者,调用者之间的桥梁

  • 类内实现的可比较器Comparable,由被比较的类implements实现,重写compareTo函数

  • 类外实现的可比较器Comparator,由另外一个类implements实现,重写compare函数

    • 如果是一次性的考虑用匿名内部类来实现比较器。否则这个只用一次的有名字的类可能会对代码理解造成困扰。

基本输入输出范例代码

1
2
3
4
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
String str1=sc.next();
System.out.println("欢迎"+n+"号同学"+str1);

第四章 流程控制

循环语句

foreach语句

1
2
3
for(元素类型x:遍历对象obj){
引用了x的java语句;
}

例子:遍历一维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Repetition{
public static void main(String args[]){
int arr[]={5,13,96};
System.out.println("一维数组中的元素分别为:");
//x的类型与arr元素的类型相同 。for循环依次取出arr中的值并赋给x
for(int x:arr){
System.out.println(x);
}
}
}

/*输出结果:
一维数组中的元素分别为:
5
13
96
*/

第五章 数组

数组的基本操作

数组排序

  • Arrays.sort(arr);
  • Arrays.parallelSort(arr);多线程排序,数据量大于一百万

复制数组

copyof()方法

语法:int a[]=Arrays.copyOf(arr,int newlength);

  • newlength:复制后新数组的长度

!注:不能直接a=b,数组名是指针常量(常指针)

第六章 类和对象

面向对象概述

封装

  • 避免外部操作对内部数据的影响,提高程序的可维护性
  • 提高工作效率,把无需调用者关心的内容隐藏,简化编程,知道面对外部的接口能调用即可

对于人来说,

public:学历、知识(别人抢不走的)

protected:身体等

public:很多

protected同包其他类或子类(继承)可见,其他包的类或子类不可见;private都不可见,只有本类可见。

this 关键字

this关键字用于表示本类当前的对象,只能在本类中使用。

1
2
3
public void setName(String name){//定义一个setName()的方法
this.name=name;//将参数值赋予类中的成员变量
}

类的构造方法

Java类内的属性值不支持默认值,不能直接定义 int count=0

应使用默认构造函数初始化

1
2
3
4
5
6
7
8
9
10
public class eggCake{
int eggCount;
public EggCake(int eggCount){//有参构造
this.eggCount=eggCount;
}
public EggCake(){
//设置鸡蛋灌饼里蛋的个数为1
this(1);
}
}

静态

1
2
3
4
5
6
7
8
9
10
public class Lab2_1{

public static void main(String[] args){
new Lab2_1().show();//临时无名对象
//如果是show.()直接调用会报错,除非把show 声明为静态函数,说这是上面的方法,调用类内成员函数
}
void show(){
System.out.println("Hello!")
}
}

类内静态成员共用一份空间

如果函数

对象

对象的销毁

1
2
3
4
5
6
7
8
9
10
public class Lab2_1{

public static void main(String[] args){
Lab2_1 l2=new Lab2_1();
//l2=null; 销毁对象 之后如果是l2.show();那么会报错NullPointerException
}//超过作用域 对象l2销毁
void show(){
System.out.println("Hello!")
}
}

第七章 继承、多态、抽象类与接口

类的继承

extends 关键字

语法:CHild extends Parents

super 关键字

Object 类

Object类是一切类的基类,隐含的继承

  • 如果没声明,toString()一定是调用自Object类,输出是字符编号。

  • 输出字符串自动会调用toString()函数,应当重写以达到需要的输出目的。

  • 重写只能保持或扩大访问权限,如原本是Public不能改成Private。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Student{
String name;
int age;

public Student(String name,int age){
this.name=name;
this.age=age;
}

@Override//加这个帮助检查重写的函数名是否正确
public String toString(){
return "我叫"+name+",今年"+age+"岁。";
}
public static void main(String[] args){
Student s1=new Student("张三",16);
System.out.println(s1);//就是System.out.println(s1.toString());
}
}

对象的类型转换

需要基类对象的任何地方,都可以用派生类对象替代

向上转型

向下转型

instanceof关键字判断对象类型

方法的重载

函数的返回值类型不属于重载的依据

final 关键字

最终的、终态

多态

抽象类与接口

抽象类

  • 只要类中有一个抽象方法,此类就是抽象类
  • 抽象类不能实例化,抽象类存在的目的就是为了被继承
  • c++中全是抽象方法就叫纯虚类

接口

  • 只能声明,不能实现

第9章 异常处理

异常与错误:

一个不好的问题发生了,如果对该问题提前有应对措施,就是异常处理

如果没有任何准备,就是错误

1
2
3
4
5
6
7
8
9
10
try{
//想要正确执行,但是不可控的语句段
//数据输入输出、网络连接、文件读写、数据库连接访问
}catch(Exception e){
//可以有多个catch语句,捕获不同的异常
//必须是小范围异常(异常类)在前,Exception必须放在最后,Exception是所有异常的基类,范围最大
}
finally{
//不管是否有捕获异常,都想要执行的代码
}

第10章 字符串

String类,字符常量存储。

创建

1
2
char a[]={'g','o','o','d'};
String s = new String(a);//相当于String s = new String("good")

截取

1
2
char a[]={'g','o','o','d'};
String s = new String(a,1,2);//相当于String s = new String("oo")

字符串连接

int+''int转为String类型

获取字符串信息

1
2
System.out.println(s[0]);//是错误的
System.out.println(s.charAt[0]);//才对

字符串的查找

indexOf(String s) 区分大小写

1
2
3
String str="We are students";
int size=str.indexOf("a");
int size2=str.toLowerCase().indexOf("a");//转为小写再查找,这个toLowerCase()或toUpperCase()是生成新的字符串对象,不会修改原来的字符串

字符串操作

获取字符串

substring(int beginIndex)

1
2
String str="Hello World";
String substr=str.substring(3);

字符串替换

replace()

replaceAll()支持正则表达式

判断相等

equals()而不是用==

1
2
3
4
5
6
String s1="abc";
String s2="abc";
String s3=new String("abc");
String s4=new String("abc");
System.out.println(s1==s2);//结果是true
System.out.println(s3==s4);//结果是false

如果直接等号赋值,就是基本数据类型,用==可以判断;当用new,把变量当对象来看待,两个对象不可能相等,因此只能用equals()来判断相等。

equals在基本数据类型比较的是值,引用数据类型对象、数组、函数比较的是地址,如果要比较值需要重写equals。String是已经重写好equals了的

没重写之前两者都是比较地址,重写之后前者比较地址后者比较值

比如我们写一个person类,根据姓名来判断两个person类实例对象是否相等。

1
2
3
4
5
6
7
8
@Override
public boolean equals(Object obj) {
if (obj instanceof Person) {
Person person = (Person) obj;
return name.equalsIgnoreCase(person.getName().trim());
}
return false;
}

正则表达式

1
2
String regex1="[a-zA-Z_$]+[a-zA-Z_$]*";//
String regex2="[1-9][0-9]{4,10}*";//qq号码规则

使用:

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
package lesson5;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegExp {

public static void main(String[] args) {
// TODO 自动生成的方法存根
String contentString="1998,1239,12,144";
//1.\\d表示一个任意的数字
String regString="\\d\\d\\d\\d";//找四个连续的数字,分组的话是(//d)(//d)
//2.创建模式对象[即正则表达式对象]
Pattern pattern=Pattern.compile(regString);
//3.创建匹配器
//说明:创建匹配器matcher,按照正则表达式的规则去匹配content字符串
Matcher matcher=pattern.matcher(contentString);
//4.开始匹配
/*
* match.find()完成的任务 (考虑分组,如(//d//d)(//d//d))
* 1. 根据给定的规则,定位满足规则的字符串(如1999,分组即(19)(99))
* 2. 找到时,将字符串的开始的索引记录到matcher对象的属性 int[] groups;
* 2.1groups[0]=0,把该子字符串的结束的索引+1的值记录到group[1]=35,+1是因为取字符串左闭右开
* 2.2 记录第一组()匹配的字符串的区间group[2]=0,group[3]=2
* 2.3 记录第二组()匹配的字符串的区间group[4]=2,group[5]=4
* 2.4 如果有更多分组以此类推
* 3. 同时记录oldLast的值为子字符串的结束的索引+1的值即5,那下一次执行find时,从5开始匹配
*/
while(matcher.find()) {
System.out.println("找到:"+matcher.group(0));//group(0)记录找到的字符串开头,同时可以找到结尾
System.out.println("第一组()的值: "+matcher.group(1));//19
System.out.println("第二组()的值: "+matcher.group(2));//99
}
}

}

元字符

检索特殊字符要用转义符号\\,在java的正则表达式中两个\\代表其他语言一个\

字符匹配符

符号 含义 实例 说明 匹配输入
[ ] 匹配任意一个 [efgh] e,f,g,h任意一个 e,f,g,h
[^ ] 排除 [^abc] 除了abc之外的任意一个字符包括数字和特殊符号 d,f,p
. 匹配除\n以外的任何字符 a..b a开头b结尾中间任意两个字符 aaab,a#*b
\\d 匹配单个数字字符相当于{0-9} \\d{3}{\\d}? 包含3个或4个数字的字符串 123,9876
\\D 匹配单个非数字字符,相当于[^0-9] \\D{\\d}* 以单个非数字字符开头后接任意个数字的字符串 a,A342
\\w 匹配单个数字、大小英文写字母,下划线,相当于[0-9a-zA-Z_] \\d{3}\\w{4} 以3个数字字符开头的任意长度为7的数字字母字符串 234abcd、1234Pe
\\W 匹配单个非数字、大小写字母和下划线字符,相当于[^0-9a-zA-Z_] \\W+\\d{2} 以至少1个非数字字母字符开头,2个数字字符结尾的字符串
\\s 匹配任何空白字符(空格,制表符等)
\\S 匹配任何非空白字符
\\. 匹配除\n之外的所有字符

区分大小写

默认区分大小写,如模式串写"abc"匹配出来abc而(?i)abc表示不区分大小写,a(?i)bc是bc不区分大小写

常用类库

Integer类

能在int类型和String类型之间互相转换。

1
2
3
int num = Integer.parseInt("456");//返回包含在由str指定的字符串中的数字的等价整数值
Integer iNum=Integer.valueOf("456");//返回保存指定的String值的Integer对象
iNum.equal(num);//比较,返回值是true

Double类

是Number类的子类,都是对浮点数进行操作。Double类在对象中包装一个基本类型为double的值,每个Double类的对象都包含一个double类型的字段。可将String和double相互转换。

1
2
3
Double dNum=Double.valueOf("3.14");
Double.isNaN(dNum.doubleValue());//是否返回非数字(NaN)值
dNum.intValue();//转为int类型

Boolean类

当 String 的参数值在不区分大小写的时候等于 “true” ,则 Boolean.valueOf(String) 返回值为 true;

1
2
3
4
Boolean b1=Boolean.valueOf("true");
Boolean b2=Boolean.valueOf("ok");
b1.booleanValue();//将Boolean对象的值以对应的boolean值返回,值为true
b2.booleanValue();//值为false

Character类

字符大小转小写

1
2
3
4
Character mychar1=Character.valueOf('A');//返回保存指定char值的Character对象
Character.isUpperCase(mychar1);//判断是否为大写字母
Character.toUpperCase(mychar1);//转为大写
Character.toLowerCase(mychar1);//转小写,不改变原来字符串

第12章 集合类

graph LR
A[集合类]-->集合类概述
A-->Collection接口
A-->List集合
A-->Set集合
A-->Map集合

集合类概述

集合与数组

容器 数组 集合
长度 长度固定 长度可变
存放的东西 存放基本数据类型 存放对象的引用
graph LR
HashMap-->A[Map]
TreeMap-->A
HashSet-->B[Set]
TreeSet-->B
ArrayList-->C[List]
LinkedList-->C
C-->D[Collection]
B-->D
A-->Java.lang.Object
D-->Java.lang.Object

Collection 接口

1
Collection<类名> list=new ArrayList<>();//尖括号只能呢是类,比如不能放int要放integer

方法:

  • add(E e)
  • remove(Object o)
  • isEmpty()
  • iterator()
  • size() 返回int型

遍历集合通过迭代器(Iterator)来实现,只读且向前。Collection接口中的iterator()方法可返回在此Collection进行迭代的迭代器。

可以用foreach,可读可写,但是不能break,除非抛出异常。遍历修改迭代器(ListIterator)

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.*;
public class Muster{
public static void main(String args[]){
Collection<String> list=new ArrayList();
list.add("《Java从入门到精通》");
list.add("《java实战》");
lteratior<String> it=list.lterator();
while(it.hasNext()){
String str=(String)it.next();//获取集合中的元素
System.out.println(str);
}
}
}

List集合

List集合中的元素允许重复,各元素的顺序就是对象插入的顺序。类似Java数组,用户通过使用索引(元素在集合中的位置)来访问集合

List接口

继承Collection接口,包含其所有方法。还定义两个重要的方法:

  • get(int index):获取指定索引位置的元素
  • set(int index,Object obj):将集合中指定索引位置index的对象修改为指定的对象obj

List接口的实现类

属于有序集合,不是自动排序的意思

  • ArrayList类 可变的数组,允许重复 允许null
  • LinkedList类链表结构保存对象
ArrayList LinkedList
优点 根据索引快速访问 便于向集合插入和删除对象
缺点 插入或删除对象速度较慢 随机访问效率较低

动态数组,索引存取

链表,存取有序,不是存到链表头就是链表尾

实例化

1
2
List<E> list=new ArrayList<>();
List<E> list2=new LinkedList<>();

开头是List,后面别忘了<>

Set集合

Set集合传入的Collection对象不能有重复值

Set接口实现的类:

  • HashSet类,由哈希表(HashMap实例)支持。允许null
  • TreeSet类,还实现了Java.util.SortedSet接口
HashSet TreeSet
区别 不保证Set集合的迭代顺序和顺序的恒久不变 按自然顺序递增,也可按比较器实现排序
1
Set<类名> set=new TreeSet<>();

必须可比较,传入的对象的类必须是包装类(默认字典顺序)或者实现comparable接口的类

HashSet哈希存储,计算哈希值散列导不同位置,存取位置不能保证,效率高

TreeSet树存储,按照树结构对元素进行比较,放到合适位置,这也就说明,元素会按照树的性质去存储,那么也就无法保证存和取元素的顺序。但是元素可以在存储的时候根据自身的大小排好序,从而可以很轻易的找到最大值,最小值,以及给定一个元素,找到比他大和比他小元素等操作。

问:在定义对象类型时,应该定义为基类或接口的类型,还是派生类的类型?

  • 创建的时候类型不确定,先定义为基类;或如果定义为接口给别人调用就声明为基类。要专门调用派生类具有的功能直接声明为派生类。

  • 但是定义为基类无法调用子类特有的成员,如subSet()TresSet特有的,定义为基类时无法调用,除非强制转型为子类。

    1
    2
    Set<Integer> set=new TreeSet<>();
    TreeSet set2=((TreeSet<Interger>)set.)subSet(3,10);

TreeSet增加的方法:

  • first() 返回此Set集合第一个(最低)元素
  • last() 返回当前最后一个(最高)元素
  • comparator() 返回进行排序的比较器,若自然顺序则null
  • headSet(E toElement) 返回一个新Set集合是toElement对象(不包含)之前的所有对象
  • subSet(E fromElement,E toElement) 返回Set集合是fromElement对象与toElement之间的所有对象,含头不含尾
  • tailSet(E fromElement) 返回包含fromElement之后所有对象

含头不含尾

Map集合

没有继承Collection接口,每个对象是键值(<key,value>)的形式。

每个key只能映射一个value;类要可比较;iterator()的next()输出的仅是key值

Map接口除集合方法的特殊方法

  • put(K key,V value)
  • containsKey(Object key) 若包含指定key的映射关系返回true
  • containsValue(Object value) 若将一个或多个key映射到指定值,返回true
  • get(Object key) 返回对象对应的值,否则null
  • values() 返回该集合所有值对象形成的Collection对象,用iterator()遍历输出
HashMap TreeMap
特点1 允许nul值和null键,键唯一 不允许值对象null(要排序的原因)
映射 通过哈希表 具有一定顺序
优缺 不保证顺序不变;快速访问 添加、删除、定位性能差;顺序排序

运用的时候使用HashMap类实现Map集合,当需要顺序输出时再创建一个完成相同映射关系的TreeMap类实例

1
2
Map<String,String> map=new HashMap();
map.put("1001","java从入门到精通");

如果key传入的是像Integer,String这样本身有序的,就会按字典序排列,而不是按哈希码。

原理:add()时用hashcode找哈希地址,用equal()看是否有存东西。如果是false就先并排放;若是true说明有了不用重复存。取值的时候找哈希地址,如果存在多个用equal()取具体的那一个。以上哈希码相同的情况为哈希冲突,同一个哈希地址可以存放多个不同对象。

优点:不用全部遍历,找到哈希地址再判断哪个是需要的取出即可。

第13章 枚举类型与泛型

将“填空题”变为“选择题”

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
//声明为int/String类型方便switch case用
//接口
interface SeasonInterface{
int Spring=1,SUMMER=2,AUTUMN=3,WINTER=4;
//开头隐含的public static final
}
//枚举
enum SeasonEnum{
SPRING,SUMMER,AUTUMN,WINTER
}
public class SeasonDemo{
public static void printSeason1(int season){
switch(season){
case SeasonInterface.SPRING:
System.out.println("这是春季");
break;
case SeasonInterface.SUMMER:
System.out.println("这是夏季");
break;
case SeasonInterface.AUTUMN:
System.out.println("这是秋季");
break;
case SeasonInterface.WINTER:
System.out.println("这是冬季");
break;
}
}
}

问:接口和枚举如何选择?

  • 接口一般是给别人来实现,功能可以更强大,如果不需要实现直接用枚举就行。
  • 枚举优点:简单,运行效率高,类型安全

枚举类型中的构造方法

在枚举类型中,可以添加构造方法,但是规定这个构造方法必须被private修饰符所修饰。用于提示枚举值更加详细的含义、

泛型

向上向下转型

1
2
3
父类 a=new 子类();//向上转型
子类 b=(子类)a;//向下转型
子类 b=new 父类();//!这是不可行的!

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test{
private Object b;
public Object getB(){
return b;
}
public void setB(Object b){
this.b=b;
}
public static void main(String[] args){
Test t=new Test();
t.setB(Boolean.valueOf(true));//向上转型
System.out.println(t.getB());
t.setB(Float.valueOf("12.3"));
Float f=(Float)t.getB();//向下转型
System.out.println(f);
}
}

定义泛型类

Object类为最上层的类,为了通用通常使传入的值与返回的值都以Object类型为主(Object太大了,不能统统用Object类)。当需要使用这些实例时,必须正确地将该实例转换为原来的类型,否则运行时将会发生ClassCaseException为了预防,Java提供泛型机制:

1
类型<T>

高级用法

泛型限制

对泛型类的实例类型做了限制

1
2
3
4
5
6
7
8
public class LimitClass<T extends List>{
public static void main(Stirng[] args){
//可以实例化已经实现List接口的类
LimitClass<ArrayList> l1=new LimitClass<ArrayList>();
//这句是错误的,因为HashMap类没有实现List()接口
LimitClass<HashMap> l3=new LimitClass<HashMap>();
}
}

<T extends Serializable> 序列化,泛型必须是Serializable的子类,可传IntegerString

泛型通配符

1
2
3
A<?> a;
A<? extends anyClass> a;
A<?> a= new List<>;//这样是不可以的,实例化需要要具体的类型

第14章 lambda表达式与流处理

将lambda表达式用来简化表示匿名函数,也就是没有名字的函数,提高开发效率

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//函数式接口
interface A{
void action();
}
//使用匿名类创建对象
A a=new A(){
public void action(){
System.out.println("创建了接口的匿名对象");
}
};
//使用lambda表达式创建
A a=()->{
System.out.println("创建了接口的匿名对象");
};

lamba表达式:

1
2
3
4
()->结果表达式
参数->结果表达式
//多形参
(参数1,参数2,...,参数n)->结果表达式

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//函数式接口
interface AdditionInterface{
int add(int a,int b);
}

public class ParamterDemo{
public static void main(String[] args){
//lamba 表达式实现加法接口,返回参数相加的值(自动加return)
AdditionInterface np=(x,y)->x+y;
int result=np.add(15,26);//调用接口方法
System.out.println("相加结果:"+result);//输出相加结果
//而同一个接口可以实现不同的方法,关键在于接口的实现方式
AdditionInterface np2=(a,b)->a*b;
result=np2.add(a,b);
System.out.println("相乘结果:"+result);//输出相乘结果
}
}

注意:

  • lamba表达式不能修改局部变量的值,只能使用

方法的引用

引用静态方法(复制一个已有的函数实现接口,甚至可以不需要这个已有函数内部是如何实现的)

类名::静态方法名

1
2
3
4
interface StaticMethodInterface{
int method(int a,int b);
}
public class StaticM

Function接口

Function<T,R>,T:被操作的类型,可以理解为方法的参数类型;R:操作结果类型,方法的返回类型。

Iterator<T>只读!

ListIterator<T>才能修改list的元素

流处理

Stream流只能被消费一次,之后失效

数据过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
//数据转化为流
public class FilerDemo{
public static void main(String[] args){
List<Employee> list=Employee.getEmpList();
Stream<Employee> stream=list.stream();
stream=stream.filter(people->people.getAge()>30);//过滤出符合条件的数据
List<Employee> result=stream.collect(Collectors.toList());//将流对象重新封装成一个List集合
List<Employee> result2=list.stream().filter(p->p.getDept().equals("开发部")).peek(p->p.setSalary(p.getSalary()*10)).collect(Collect.toList());//过滤得到符合的让其工资翻十倍
for(Employee emp:result){
System.out.println(emp);//输出员工信息
}
}
}

filter(predicate类)用lamba表达式不需要管类型,直接放进去

peek(consumer类型)和map()一个里面不用返回值,一个需要。

collect() 收集重新归类。

第15章 I/O(输入与输出)

graph LR
A[I/O]-->输入/输出流
A-->File类
A-->文件输入/输出流
A-->带缓存的输入/输出流
A-->数据输入/输出流

输入/输出流

程序从指向源的输入流中读取源中的数据。

各种数据源通过输入流传递到目的地

源通过数据流传递到各种数据输出目标

InputStream类用来处理字节,不适合处理字符。而Java字符是Unicode编码,双字节,用Reader类处理。但注意Reader类不是InputStream的替换者,只是在处理字符串时简化了编程。

read(byte[] b);返回值是读取到的字节数

File类

File类是java.io包中唯一代表磁盘文件本身的类。因为是代表磁盘,操作要用try catch语句

三种构造方式:

1
2
3
File(String pathname)
File(String parent,String child)
File(File f,String child)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.File;
public class FileTest{
public static void main(String[] args){
File file=new File("word.txt");//相对路径,是在项目目录下创建而不是在所在包类,此时是在内存中创建对象
//File file2=new File("D:\\1.txt");//绝对路径
if(file.exists()){//如果存在
file.delete();
System.out.println("文件已删除");
}else{
try{
file.createNewFile();//这一步才真正在磁盘中创建文件
System.out.println("文件已创建");
}catch(Exception e){
e.printStackTrace();
}
}
}
}

File类创建的是一个文件对象!说是在磁盘创建是错误的;除了在内存中的操作,其他都是不保证成功的,

  • getName() 文件名称
  • length() 文件的长度(以字节为单位)
  • isHidden() 判断是否隐藏文件,返回布尔值

文件输入/输出流

FileInputStream与FileOutputStream类

是字节流,读取写入参数得是字节byte,读取汉字容易乱码

  • 创建一个FileOutputStream对象时,可以指定不存在的文件,但是不能是已被其他程序打开的文件
  • 文件输入流FileInputStream类实现!读取!,用read(byte[] b),从磁盘输入到目的地
  • 文件输出流FileOutputStream类实现!写入!,用write(byte[] b),从程序输出到磁盘

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class FileStreamDemo{
public static void main(String[] args){
File file=new File("D:\\word.txt");//创建文件对象
//写入
try{
FileOutputStream out=new FileOutputStream(File);//创建输出对象
byte buy[]="我有一只小毛驴".getBytes();//写入内容的字节数组,字符串转转字节
out.write(buy);//将字节写入到文件
out.close();//关闭流
}catch(IOException e){
e.printStackTrace();
}
try{
FileInputStream in=new FileInputStream(file);//创建输入流
byte byt[]=new byte[1024];//缓存字节流
int len=in.read(byt[]);//字节转字符串要用
System.out.println("文件的信息是:"+new String(byt,0,len));//字节转为字符串输出
in.close();
}catch(IOException e){
e.printStackTrace();
}
}
}

FileReader和FileWriter

是字符流,读取写入参数字符串即可

1
2
3
4
5
6
7
8
9
10
11
12
File file=new File("D:\\word.txt");//创建文件对象
//写入
FileWriter fw=new FileWriter(file);
String word="我有一只小毛驴";
fw.write(word);
fw.close();
//读取
FileReader fr=new FileReade(file);
char ch[]=new char[1024];//缓存字符数组
int len=fr.read(ch);
System.out.println("文件的信息是:"+new String(ch,0,len));//字节转为字符串输出
fr.close();

带缓存的输入/输出流

缓存是I/O的一种性能优化。缓存流增加了内存缓冲区,使得在流上执行skip()mark()reset()方法都成为可能。

BufferedReader与BufferedWriter类

分别继承Reader类和Writer类,以行为单位进行输入/输出

读取文件过程:

文件–>InputStream–>InputStreamReader–>BufferedReader–>字符数据

BufferReader类常用的方法:

  • read() 读取单个字符
  • readLine() 读取一个文本行,返回字符串型。若无返回null

BufferWriter类的方法都返回void:

  • write(String s, int off, int len) 写入字符串的一部分
  • flush() 刷新流的缓存
  • newLine() 写入一个行分隔符

在使用BufferedWriter类的Writer()方法时,数据首先进入缓存区,没有立刻被写入输出流。如果想立即将缓存区中的数据写入输出流,一定要调用flush()

1
BufferedReader br = new BufferedReader(new FileReader(file));//缓存输入流

实例

基本文件操作
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
public class test2 {
public static void main(String[] args) {
String content[]= {"好久不见","最近好吗","常联系"};
File file=new File("word.txt");
try {
BufferedWriter bWriter=new BufferedWriter(new FileWriter(file));//文件字符输出流转缓冲输出流
for (int k = 0; k < content.length; k++) {
bWriter.write(content[k]);//写入字符串
bWriter.newLine();//写入一个换行符
}
bWriter.close();//关闭缓冲输出流
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
try {
BufferedReader bReader=new BufferedReader(new FileReader(file));//文件字符输入流转缓冲输入流
String tmpString=null;//作为缓冲的临时字符串
int i=1;//行数
//从文件重读取一行,如果读出内容不为null,则进入循环
while ((tmpString=bReader.readLine())!=null) {
System.out.println("第"+i+"行:"+tmpString);
i++;
}
bReader.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}

输出:
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
25
26
27
28
29
30
31
import java.io.*;
import java.net.*;

public class GetPageCode{

public static void main(String[] args) throws IOException {
// TODO 自动生成的方法存根
URL page = null;
try {
page = new URL("https://www.qq.com");
} catch (MalformedURLException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}

HttpURLConnection conn = (HttpURLConnection) page.openConnection();

InputStreamReader in = new InputStreamReader(conn.getInputStream());

BufferedReader buff = new BufferedReader(in);
String tmp=null;
int i=1;
//从流中读出一行,如果内容不为null,则进入循环
while ((tmp=buff.readLine())!=null) {
System.out.println(tmp);
i++;
}

}

}

第16章 反射与注释

实现访问、检测和修改描述Java对象本身信息的功能。

getCLass()是Object类定义的,任何类都可以用,获取类信息

1
2
3
4
5
6
7
8
9
10

Class newC=hashSet.getClass();//获取hashSet的描述信息,假设hashSet是一本书,那newC是书的目录,作者等等基本信息但不包括内容
//查构造方法
Constructor[] constructors=mewC.getDeclaredConstructor;
for(Constructor constructor:constructors){
System.out.println(constructor);//找到构造函数foreach遍历显示
}
declarednewC=newC.getDeclaredConstructors();


1
2
3
4
Field[] declaredFields=demClass.getDeclaredFields();
for(Field field:declaredFields) {
System.out.println(field);
}
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
//查成员方法 用户输入参数
Method[] declaredMethods=demClass.getDeclaredMethods();
for(Method method:declaredMethods) {
System.out.println(method);
}
Scanner scanner=new Scanner(System.in);
String key=scanner.next();
for(Method method:declaredMethods) {
if(key.equals("size")&&key.equals(method.getName())) {
try {
System.out.println(method.invoke(hashSet));
} catch (Exception e) {
e.printStackTrace();
}
}
if (key.equals("add") && key.equals(method.getName())) {//作判断才知道用户要做那个
String addString = scanner.next();
try {
System.out.println(method.invoke(hashSet, addString));
for (String hash : hashSet) {
System.out.print(hash+" ");
}
} catch (Exception e) {
e.printStackTrace();
}
}

}
scanner.close();

第18章 Swing程序设计

Swing概述

graph LR
javax.swing.JLabel-->D[javax.swing.JComponent]
Javax.swing.JPanel-->D
javax.swing.JDialog-->A[java.awt.Dialog]
javax.swing.JFrame-->B[java.awt.Frame]
A-->C[java.awt.Window]
B-->C
D-->E[java.awt.Container]
C-->E
E-->F[java.awt.Component]
F-->java.lang.Object

由Swing包的层次结构和继承关系可知

  • Dialog和Frame都在awt.Window里
  • 而JDialog在awt.Dialog里,JFrame在awt.Frame里
  • JPanel和JLabel在swing.JComponent里
  • 它们都在awt.Container里,最顶层是java.lang.Object

Swing常用窗体

  • public JFrame(String title); 默认不可见窗体,可以标题

  • 将窗体转为容器

    1
    2
    JFrame jf=new JFrame("登录");
    Container container=jf.getContentPane();
  • container.add();container.remove() 添加;删除容器中的组件

  • container 是主容器,组件都在这个范围内

  • JLabel 内容可以用html标签

  • setBounds(距离左边x,距离上边y,宽度,高度) 设置窗体左上角的坐标和大小,对于JFrame是设置距离屏幕的位置,对于container里的就是相对灰色的container位置

  • setLocation(int x,int y);

  • setSize(int width, int height); 设置窗体宽高

  • setVisibale(boolean b); 是否可见

  • setDefaultCloseOperation(int operation); 设置关闭方式

    • 默认是DISPOSE_ON_CLOSE 窗体关闭释放窗体资源,窗体消失但程序不停止
    • EXIT_ON_CLOSE 窗体关闭,释放窗体资源并关闭程序

Jdialog 对话框

  • public JDialog(Frame f, Sring title,boolean mode)设置modeltrue时,打开对话框时,阻塞主窗体不可操作。

JButton单击事件

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
class MyJDialog extends JDialog{
public MyJDialog(MyFrame frame) {
super(frame,"第一个JDialog窗体",true);//调用父类构造方法,参数:父类窗体,标题,是否阻塞父窗体
Container container=getContentPane();//获取主窗体
container.add(new JLabel("这是一个对话框"));//在容器中添加标签
setBounds(120,120,100,100);//设置对话框在桌面显示的坐标和大小
}
}
public class MyFrame extends JFrame{
public MyFrame() {
Container container=getContentPane();//获取窗体主容器
container.setLayout(null);//窗体使用绝对布局
JButton bl=new JButton("弹出对话框");
bl.setBounds(10, 10, 100, 21);//按钮坐标大小
bl.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
// TODO 自动生成的方法存根
MyJDialog dialog=new MyJDialog(MyFrame.this);//创建对话框,传参是当前窗体
dialog.setVisible(true);
}
});
container.add(bl);
setSize(200, 200);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//关闭窗体停止程序
setVisible(true);
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
new MyFrame();
}

}

常用布局管理器

null 绝对布局

硬性指定位置和大小,组件位置通过绝对坐标的方式来指定

  • 首先取消布局管理器:Container.setLayout(null)
  • 设置每个组件在容器的位置和大小:Component.setBounds(int x,int y,int width,int height)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class AbsolutePosition extends JFrame{

public AbsolutePosition() {
setTitle("绝对布局");
setLayout(null);//取消布局管理器
setBounds(0,0,300,150);
Container container=getContentPane();
JButton b1=new JButton("按钮1");
JButton b2=new JButton("按钮2");
b1.setBounds(10,30,80,30);//设置按钮位置和大小
b2.setBounds(60,70,100,20);
container.add(b1);
container.add(b2);
setVisible(true);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
new AbsolutePosition();
}

}

FlowLayout 流布局管理器

  • 流布局组件从左到右摆放。当组件占据了当前行的所有空间时,溢出的到下一行
  • 默认情况,行组件排列方式为居中对齐,可以通过设置更改

FlowLayout类具有以下常用的构造方法

  • public FlowLayout()
  • public FlowLayout(int alignment)
  • public FlowLayout(int alignment,int horizGap,int vertGap)

alignment参数表示排列方式,可以设置为Flowlayout.LEFTFlowLayout.CENTERFlowLayout.RIGHT

horizGap,vertGap这两个参数以像素为单位指定组件之间的水平间隔和垂直间隔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class FlowLayoutPosition extends JFrame{
public FlowLayoutPosition() {
setTitle("流布局管理器");
Container container=getContentPane();
setLayout(new FlowLayout(FlowLayout.RIGHT,10,10));
for (int i = 0; i < 10; i++) {
container.add(new Button("button"+i));
}
setSize(300,200);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
new FlowLayoutPosition();
}

}

BorderLayout 边界布局管理器

  • Swing创建窗体默认是边界布局管理器

  • 边界布局管理器把容器分为东、南、西、北、中5个区域

  • 当组件添加时,需要使用BorderLayout类中的成员变量指定其区域

    • BorderLayout.NORTH 北

    • BorderLayout.SOUTH 南

    • BorderLayout.EAST 东

    • BorderLayout.WEST 西

    • BorderLayout.CENTER 中

      ———–NORTH———–

      WEST—CENTER—-EAST

      ———–SOUTH———–

  • add(组件,成员变量)

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
public class BorderLayoutPosition extends JFrame{
public BorderLayoutPosition() {
setTitle("边界布局管理器");
Container container=getContentPane();
setLayout(new BorderLayout());//使用边界布局管理器
JButton centerButton=new JButton("中");
JButton northButton=new JButton("北");
JButton southButton=new JButton("南");
JButton westButton=new JButton("西");
JButton eastButton=new JButton("东");
container.add(centerButton,BorderLayout.CENTER);
container.add(eastButton,BorderLayout.EAST);
container.add(westButton,BorderLayout.WEST);
container.add(southButton,BorderLayout.SOUTH);
container.add(northButton,BorderLayout.NORTH);
setSize(350,200);
setVisible(true);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
new BorderLayoutPosition();
}

}

GridLayout 网格布局管理器

  • 划分为网格,组件可以按行、列进行排序
  • 网格个数由行数和列数决定,每个网格大小相同
  • 组件从网格左上角开始,从左到右从上到下被添加到网格中
  • 每个组件都会填满整个网格
  • 改变窗体大小,组件大小也会随之改变

构造方法

  • public GridLayout(int rows, int columns)
  • public GridLayout(int rows, int columns, int horizGap, int vertGap)

rows和columns只有一个可以是0,被用于一行或一列排列任意多个组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class GridLayoutPosition extends JFrame{
public GridLayoutPosition() {
Container container=getContentPane();
setLayout(new GridLayout(7,3,5,5));//7行3列网格,组件水平间距5像素,垂直间距5像素
for (int i = 0; i < 20; i++) {
container.add(new JButton("button"+i));
}
setSize(300,300);
setTitle("网格布局管理器");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
// TODO 自动生成的方法存根

}

}

常用面板

JPanel 面板

必须在窗体容器中使用,无法脱离窗体显示

1
Jpanel p1=new JPanel(new GridLayout(1,4,10,10));//初始化面板,使用1行4列的网格布局,组件水平间隔10像素,垂直间隔10像素

JScrollPane 滚动面板

1
2
3
4
Container c=getContentPane();
JTextArea ta=new JTextArea(20,50);//创建文本区域组件,文本域默认大小为20行、50列
JScrollPane sp=new JScrollPane(ta);//创建滚动面板,并将其文本域放到滚动面板中
c.add(sp);

图片路径

1
2
3
Icon con=new ImageIcon("src/注意.png");//使用字符串作为路径,是以项目文件夹为根目录
URL url=MyImageIcon.class.getResourse("注意.png");//getResourse是以类所在文件夹为根目录
Icon icon=new ImageIcon(url);//创建Icon对象

当用new File()时相对路径是相对于项目的路径,例如JavaSE下面有src,src下面有包,包里有类,当在类中用new File()相对路径访问src下的文件时应该是

1
File file=new File("src/[文件名]");

事件监听器

ActionEvent动作事件

动作事件监听器

相关定义 实现方式
事件名 ActionEvent
事件源 JButtonJListJTextField等组件
监听接口 ActionListener
添加监听方法 addActionListener()
删除监听方法 removeActionListener()
1
2
3
4
5
6
7
8
9
JButton b1=new JButton("按钮");
bl.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
// 要触发的动作

}
});

或者用类调用接口的方法:

1
2
3
4
5
6
7
8
9
10
11
public class SimpleEvent extends JFrame{
private JButton jb=new JButton("我是按钮");
public SimpleEvent(){
jb.addActionListener(new jbAction());
}
class jbAction implements ActionListener{
public void actionPerformed(ActionEvent arg0){
jb.setText("我被点击了");
}
}
}

jbAction实现ActionListner接口,同时在该内部类中实现actionPerform()方法,这个方法中定义当用户单击该按钮后实现怎样的功能

KeyEvent键盘事件

KeyEvent类负责捕获键盘事件,可以通过为组件添加实现了KeyListener接口的监听器类来处理相应的键盘事件。

KeyListerner接口3个抽象方法

1
2
3
4
5
6
7
8
public interface KeyListener extends EventListener{
//发生击键事件时触发
public void keyTyped(KeyEvent e);
//按键被按下时被触发
public void ketPressed(KeyEvent e);
//案件被释放时被触发
public void keyReleased(KeyEvent e);
}

KeyEvent类中的常用方法

  • getKeyChar() 获得与此事件中的键相关联的字符
1
2
3
4
5
6
textField=new JTextField();
textField.addKeyListener(new KeyAdapter(){//文本框添加键盘事件的监听
public void keyPressed(KeyEvent e){//按键按下时触发
//实现的功能
}
})

MouseEvent鼠标事件

所有组件都能发生鼠标事件。添加MouseListener接口监听,有5个抽象对象

1
2
3
4
5
6
7
public interface MouseListener extends EventListener{
public void mouseEntered(MouseEvent e);//光标移入组件时触发
public void mousePressed(MouseEvent e);//鼠标按键按下
public void mouseReleased(MouseEvent e);//鼠标按键释放时被触发
public void mouseClicked(MouseEvent e);//发生单击事件被触发
public void mouseExited(MouseEvent e);//光标移出组件时被触发
}

注意单击事件如果按键在移除组件之后才被释放,则不会触发单机事件

MouseEvent类中的常用方法

  • getSource() 获得触发此次事件的组件对象,返回值为Object类型
  • getButton() 获得按键的int值
  • getClickCount() 获得单击按钮的次数

按键的int值:

静态常量 常量值 代表的键
BUTTON1 1 鼠标左键
BUTTON2 2 鼠标滚轮
BUTTON3 3 鼠标右键
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void mouseOper(MouseEvent e){
int i=e.getButton();//获得按键的int值
if(i==MouseEvent.BUTTON1)
System.out.println("按下的是鼠标左键");
else if(i==MouseEvent.BUTTON2)
System.out.println("按下的是鼠标滚轮");
else if(i==MouseEvent.BUTTON3)
System.out.println("按下的是鼠标右键");
}
public MouseEvent_Example(){
final JLable label=new JLable();
label.addMouseListener(new MouseListener(){
public void mouseEntered(MouseEvent e){
System.out.println("光标移入组件");
}
public void mousePressed(MouseEvent e){
System.out.print("鼠标按键被按下");
mouseOper(e);
}
})
}

第二十章 多线程

多种活动同时进行的思想称为并发,而将完成的每一件事情称为线程

CPU在一个时间片中执行某个进程,然后下一个时间片又跳至另一个进程中去执行。由于CPU转换较快,好像同时执行一样

创建线程

  • Thread类是java.lang包中的一个类
  • 完成线程真正功能的代码放在类的run()方法里
  • start()方法会启动线程,线程自动执行run()方法里的代码。
  • 如果不调用start()方法,线程永远都不会启动,在主方法没有调用start()前,Thread对象只是实例,不是真正的线程
  • 主方法线程由Java虚拟机负责启动

如果start()方法调用一个已经启动的线程,抛出IllegalThreadStateException异常

实现Runnable()接口

当要继承其他非Thread类,通过Runnable接口来实现多线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Threadtest extends Object implements Runnable{
@Override
public void run() {
// TODO 自动生成的方法存根
String string=Thread.currentThread().getName();
for (int i = 0; i < 10; i++) {
System.out.println(string+"第"+i+"次操作");
}
}
public static void main(String[] args) {
Threadtest threadtest=new Threadtest();
Thread t1=new Thread(threadtest,"线程1");//第二个是Thread的名字,一般是Thread(String name)
Thread t2=new Thread(new Threadtest(),"线程2");//匿名方式
t1.start();
t2.start();
}
}

实现Runnable()接口的程序会创建一个Thread对象,并将Runnable对象与Thread对象相关联

使用Runnable接口启动新的线程步骤如下:

  1. 建立Runnable对象
  2. 使用参数为Runnable对象的构造方法创建Thread实例
  3. 调用start()方法启动线程
1
2
3
4
5
6
Thread thread=new Thread(new Runnable(){
@Override
public void run(){

}
})

Swing与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
public class SwingAndThread extends JFrame{
int count=0;//图像横坐标
public SwingAndThread() {
setBounds(300,200,250,100);
Container container=getContentPane();//主容器
container.setLayout(null);

Icon icon=new ImageIcon("src/1.gif");
JLabel jLabel=new JLabel(icon);
jLabel.setBounds(10,10,200,50);
Thread thread=new Thread() {//匿名线程对象
public void run() {
while (true) {
jLabel.setBounds(count,10,200,50);//将标签的横坐标用变量表示
try {
Thread.sleep(500);//休眠500毫秒
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
count+=4;//横坐标每次增加4
if(count>=200) {
count=10;//到达最右边时使其回到最左边
}
}
}
};
thread.start();
container.add(jLabel);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
new SwingAndThread();
}

}

线程的生命期

  • 出生状态就是线程被创建时处于的状态,在用户使用该线程实例调用start()方法之前线程都是出生状态。
  • 当调用start()方法后,线程处于就绪状态(可执行状态)
  • 当线程得到系统资源进入运行状态
  • 处于运行状态调用Thread类中的wait()方法时,进入等待状态。进入等待状态必须由其他的线程调用notify()唤醒,自己无法唤醒自己notifyAll()方法将所有处于等待状态下的线程唤醒
  • sleep()休眠状态,时间到了会自动唤醒,区别于等待状态。
  • 线程中运行状态下发出输入/输出请求时,该线程将进入阻塞状态。等待输入输出结束时线程进入就绪状态
  • 当线程的run()方法执行完毕时,线程进入死亡状态
graph TD
出生-->A[就绪]
A-->B[运行]
B-->A
B-->C[等待]
B-->D[休眠]
B-->E[阻塞]
B-->死亡
C-->A
D-->A
E-->A

操作线程的方法

线程的加入

join()方法加入到另外一个线程。例如存在一个线程A,现在需要插入B要求线程B先执行完毕,然后再执行线程A,调用join(),类似插入队伍。

当某个线程使用join()加入另外一个线程时,另一个线程会等待该线程执行完毕后再继续执行。

使用方法:在某一个线程A的run函数中使用B.join()插入B线程。

插入到主线程:

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(Stirng[] args){
类 A=new 类();
}
public 类{
线程实例化;
线程.start;
try{
线程.join();//这样就插入到主线程
}catch(..){
e.printStackTrace();
}
}

线程的优先级

  • 每个线程具有各自的优先级,可以表明在程序中该线程的重要性
  • 系统根据优先级决定首先使哪个线程进入运行状态
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
public class Priority implements Runnable{
String nameString;
public Priority(String name) {
this.nameString=name;
}
@Override
public void run() {
// TODO 自动生成的方法存根
String tmpString="";
for (int i = 0; i < 50000; i++) {
tmpString+=i;//进行50000次字符拼接
}
System.out.println(nameString+"线程完成任务");
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Thread aThread=new Thread(new Priority("A"));
aThread.setPriority(1);
Thread bThread=new Thread(new Priority("B"));
bThread.setPriority(3);
Thread cThread=new Thread(new Priority("C"));
cThread.setPriority(7);
Thread dThread=new Thread(new Priority("D"));
dThread.setPriority(10);
aThread.start();
bThread.start();
cThread.start();
dThread.start();
}

}
//结果
D线程完成任务
B线程完成任务
C线程完成任务
A线程完成任务

由输出结果,不一定按优先级,知识作为CPU的参考依据。执行顺序由CPU决定

线程同步

线程安全

线程安全问题来源于两个线程同时存取单一对象的数据

线程同步机制

解决资源共享问题

同步块

synchronized(Object){}

通常将共享资源的操作放置synchronized定义的区域内,当其他线程获取这个锁时,就必须等待锁被释放后才可以进入该区域。其中Object有标志位,0和1,若为0,表示此同步块内存在其他线程,这是当前线程处于就绪状态,直到同步块中的线程执行完同步块代码后,该对象标志位设置为1,当前线程开始执行同步块。

在run()里边添加同步块:

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
public class SynchronizedTest implements Runnable{
int num=10;
@Override
public void run() {
// TODO 自动生成的方法存根
while (true) {
synchronized (this) {
if (num>0) {
try {
Thread.sleep(100);//使当前线程休眠100毫秒
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"——票数"+num--);
}
}
}
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
//实例化类对象
SynchronizedTest test=new SynchronizedTest();
//以该类对象分别实例化4个线程
Thread tA=new Thread(test,"线程一");
Thread tB=new Thread(test,"线程二");
Thread tC=new Thread(test,"线程三");
tA.start();
tB.start();
tC.start();
}

}
//结果
线程一——票数10
线程一——票数9
线程一——票数8
线程一——票数7
线程一——票数6
线程一——票数5
线程一——票数4
线程一——票数3
线程一——票数2
线程一——票数1

如果不加同步块结果会出现负数

线程中执行n++结果不一定正确,因为n++不是原子操作,其中包含三步包括取值,加一,赋值,中间可能会被打断,导致结果不一定准确。可以加入锁解决

1
2
3
4
5
6
7
synchronized(Object){
try{
n++;
aomicinteger.incrementAndGet();
longAdder.increment();
}
}

同步方法

在方法前面用synchronized关键字修饰方法:

1
synchronized void f(){}

同上面的例子实现一样的功能,不过运用同步方法而不是同步块:

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
public class SynchronizedTest implements Runnable{
int num=10;
public synchronized void doit() {//定义同步方法
if (num>0) {
try {
Thread.sleep(100);//使当前线程休眠100毫秒
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"——票数"+num--);
}
}
@Override
public void run() {
// TODO 自动生成的方法存根
while (true) {
doit();
}
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
//实例化类对象
SynchronizedTest test=new SynchronizedTest();
//以该类对象分别实例化4个线程
Thread tA=new Thread(test,"线程一");
Thread tB=new Thread(test,"线程二");
Thread tC=new Thread(test,"线程三");
tA.start();
tB.start();
tC.start();
}

}

网络通信

网络程序设计基础

在TCP/IP协议栈中,有两个高级协议,即传输控制协议(Transmission Control Protocol,TCP)与用户数据传输报协议(User Datagram Protocol,UDP)

高级协议 TCP UDP
特点 以固接连线为基础,从一端送至连接的另一端 以发送数据包的方式进行,向若干目标发送数据或接受来自若干源的数据
数据顺序 数据能够送达,且抵达数据的顺序=送出时的顺序 不保证抵达的顺序=送出时的顺序
类比 就像打电话,必须先拨号给对方,等两端确定连接后,互相才能听到对方说法,也知道对方回应的是什么 邮递员送信,可以寄出很多信给同一个人,且每封信相对独立。各封信到达的顺序并不重要,收信人接收信件的顺序也不能保证与寄出信件的顺序相同

TCP是面向连接的可靠协议,效率低,保证确实送达。UDP是面向无连接的不可靠协议,效率高,不保证数据可靠的传输。

一些防火墙或路由器可能设置不允许UDP数据传输协议

端口

类似营业厅的窗口,提供某些服务

HTTP 80

FTP 21

Tomcat 8080

MySQL 3306

套接字

套接字(Socket)用于将应用程序与端口连接起来。

客户端(应用程序<–>Socket<–>Port<-)-(->Port<–>Socket<–>应用程序)服务器

类似插座一样连接电器与电线

TCP程序

TCP协议进行通信的两个应用程序有主次之分,一个称为服务器程序,另一个称为客户机程序,服务端与客户端的交互过程如下:

  1. 服务器程序创建一个SeverSocket(服务器端套接字)对象,调用accept()方法等待客户机来连接
  2. 客户端程序创建一个Socket对象,请求与服务器建立连接
  3. 服务器接受客户机的连接请求,同时创建一个新的Socket对象与客户建立连接。随后服务器继续等待新的请求

SeverSocket可以理解为售后部门的电话;服务器端运行的Socket可以理解为客服人员;客户端运行的Socket可以理解为顾客

IntetAddress类

这是一个与ip地址相关的类,可以获取ip地址,主机地址等信息,常用方法:

  • getByName(String host) 返回InterAddress 获取Host项对应的InterAddress对象
  • getHostAddress() 返回String 获取InterAddress对象所包含的IP地址
  • getHostName() String 获取此ip地址的主机名
  • getLocalHost() InterAddress 返回本地主机的InterAddress对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Address {

public static void main(String[] args) {
// TODO 自动生成的方法存根
InetAddress ip;//创建对象
try {
ip=InetAddress.getLocalHost();//实例化对象
String localname=ip.getHostName();//获取本机名
String localip=ip.getHostAddress();//获取本机ip地址
System.out.println("本机名:"+localname);
System.out.println("本机IP地址:"+localip);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}

}
//结果
本机名:LAPTOP-6G798SAR
本机IP地址:192.168.4.1

SeverSocket类

服务器套接字

SeverSocket(int port):绑定到接口port的服务器套接字

当服务器向输出流写入信息时,客户端通过相应的输入流就能读取,反之亦然

注意accept()方法会阻塞线程的继续指行,直至接收到客户的呼叫

1
2
yu=server.accept();
System.out.println("连接中");

如果没有客户呼叫服务机,那么“连接中”语句不会执行

TCP网络程序设计

单项通信的例子,客户机通过输出流发送数据,服务器通过输入流接收数据:

服务器端:

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
public class MyServer {
private ServerSocket server;//服务器套接字
private Socket socket;//客户机套接字
void start() {
try {
server=new ServerSocket(8998);//服务器启用8998端口
System.out.println("服务器套接字已经创建成功");
while (true) {
System.out.println("等待客户机的连接");
socket=server.accept();//服务器监听客户机连接
BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true) {
String message=reader.readLine();//读取一行文本
if("exit".equals(message)) {
System.out.println("客户机退出");
break;//停止接受信息
}
System.out.println("客户机:"+message);
}
reader.close();
socket.close();
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
MyServer tcp=new MyServer();
tcp.start();
}

}

客户机端:

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
public class MyClient extends JFrame{
private PrintWriter writer;//根据套接字字节流创建的字符输出流
Socket socket;
private JTextArea area=new JTextArea();//展示信息的文本域
private JTextField text=new JTextField();//发送信息的文本框

public MyClient() {
setTitle("向服务器送数据");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container=getContentPane();
JScrollPane scrollPane=new JScrollPane(area);
getContentPane().add(scrollPane,BorderLayout.CENTER);
container.add(text,"South");
text.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
// TODO 自动生成的方法存根
writer.println(text.getText().trim());//将文本框中的信息写入流
area.append(text.getText()+'\n');//将文本框中的信息显示到文本域中
text.setText("");//清空
}
});
}
private void connect() {
area.append("尝试连接\n");
try {
socket=new Socket("127.0.0.1",8998);//连接本地计算机的8998接口
writer=new PrintWriter(socket.getOutputStream(),true);
area.append("完成连接\n");
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
MyClient client=new MyClient();
client.setSize(200,200);
client.setVisible(true);
client.connect();//连接服务器
}

}

UDP程序

UDP通信基本模式:

  • 将数据打包(数据包),然后将数据包发往目的地
  • 接收别人发来的数据包,然后查看数据包

发送数据包:

  1. 创建:使用DatagramSocket()创建一个数据包套接字。
  2. 打包:使用DatagramPacker(byte[] buf, int offset, int length, InetAddress address, int port)创建要发送的数据包。
  3. 发送:使用DatagramSocket()类的send()方法发送数据包。

接收数据包:

  1. 创建:使用DatagramSocket(int port)创建数据包套接字,绑定到指定的接口。
  2. 准备包:使用DatagramPacket(byte[] buf, int length)创建字节数组来接收数据包。
  3. 接收:使用DatagramPocket类的receive()方法接收UDP包。

DatagramPacket类

数据包,构造方法:

  • DatagramPacket(byte[] buf, int length) 指定包的内存空间和大小
  • DatagramPacket(byte[] buf, int length,InetAddress address,int port) 多指定了数据包的目标地址和端口

发送数据需指定接收方的Socket地址和端口号

DatagramSocket类

  • DatagramSocket()
  • DatagramSocket(int port)
  • DatagramSocket(int port, InetAddress addr) 适用多块网卡有多个IP地址

接收时要指定端口号一般用第二种,发送时不知道用第一种。

UDP网络程序设计

下面创建一个广播数据报程序,原理类似电台广播。广播电台需要在指定的波段和频率上广播信号,接收者也要将收音机调到指定的波段、频率,才可以收听广播内容。

广播主机程序不断向外播出信息:

image-20221224205156462

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
public class Notification extends Thread{
String weather="节目预报:八点有大型晚会,请收听";//发送的信息
int port=9898;
InetAddress iaddress=null;
MulticastSocket socket=null;//多点广播套接字,是一种DatagramPacket

@SuppressWarnings("deprecation")
public Notification() {
try {
iaddress=InetAddress.getByName("224.255.10.0");//广播组地址
socket=new MulticastSocket(port);//实例化
socket.setTimeToLive(1);//指定发送范围是本地网络
socket.joinGroup(iaddress);//加入广播组
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public void run() {
while (true) {
DatagramPacket packet=null;//数据包
byte data[]=weather.getBytes();//字符串消息的字节数组
packet=new DatagramPacket(data,data.length,iaddress, port);//将数据打包
System.out.println(weather);
try {
socket.send(packet);
sleep(3000);
} catch (IOException e) {
e.printStackTrace();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Notification w=new Notification();
w.start();//启动线程
}

}

接收广播程序:

image-20221224205211236

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public class Receive extends JFrame implements Runnable,ActionListener{
int port;//端口
InetAddress group=null;//广播组地址
MulticastSocket socket=null;//多点广播套接字对象
JButton inceBtn=new JButton("开始接收");
JButton stopBtn=new JButton("停止接收");
JTextArea inceAr=new JTextArea(10,10);
JTextArea inced=new JTextArea(10,10);
Thread thread;
boolean stop=false;//停止接收信息状态

public Receive() {
//界面设计
setTitle("广播数据报");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
thread=new Thread(this);
inceBtn.addActionListener(this);//绑定按钮ince的单击事件
stopBtn.addActionListener(this);//绑定按钮stop的单击事件
inceAr.setForeground(Color.blue);
JPanel north=new JPanel();
north.add(inceBtn);//将按钮添加到north面板
north.add(stopBtn);
add(north,BorderLayout.NORTH);//将north放置在窗体上部
JPanel center=new JPanel();//创建面板对象center
center.setLayout(new GridLayout(1,2));//设置面板布局
center.add(inceAr);//将文本域添加到面板上
center.add(inced);
add(center,BorderLayout.CENTER);//设置面板布局
validate();//刷新
//UDP部分
port=9898;
try {
group=InetAddress.getByName("224.255.10.0");//指定接收地址
socket=new MulticastSocket(port);//绑定
socket.joinGroup(group);//加入广播组
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
setBounds(100,50,360,380);
setVisible(true);
}
@Override
public void run() {
// TODO 自动生成的方法存根
while (stop==false) {
byte data[]=new byte[1024];//创建缓存字节数组
DatagramPacket packet=null;
packet=new DatagramPacket(data,data.length,group, port);//待接收数据包
try {
socket.receive(packet);//接收数据包
//获取数据包中的内容
String message=new String(packet.getData(),0,packet.getLength());
inceAr.setText("正在接收的内容:\n"+message);//将接收内容显示在文本域中
inced.append(message+"\n");//每条信息为一行
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO 自动生成的方法存根
if (e.getSource()==inceBtn) {//单击inceBtn按钮触发的事件
inceBtn.setBackground(Color.red);//设置按钮颜色
stopBtn.setBackground(Color.yellow);
if (!(thread.isAlive())) {//线程不处于”新建状态”
thread=new Thread(this);
}
thread.start();//启动线程
stop=false;//开始接收信息
}
if (e.getSource()==stopBtn) {//单击stop按钮触发的事件
inceBtn.setBackground(Color.yellow);
stopBtn.setBackground(Color.red);
stop=true;
}
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Receive receive=new Receive();
receive.setSize(460,200);
}

}