JavaSE知识


ch1.Java基础

1.1 Java特点

  • 跨平台
  • 运行的基本原理:代码用java文件,可执行文件是.class
  • 语法和C基本相同,但没有指针
  • 程序入口是main函数

下面介绍Java基本语法,主要包括变量、数据类型、运算、流程、数组。

1.2 变量和数据类型

整型:byte(1字节)、short(2字节)、int(4字节)、long(8字节,long表示的数字后加L

默认情况下,系统看到一个常量,认为是int。如果在变量范围之内,可以赋值,否则报错(丢失精度)。

实型:float(浮点)、double(双精度,应用更广)。

默认情况下,系统看到一个常量,认为是double。直接赋值给float,都会报错(丢失精度),float要专门用F来标记

字符型:char,用法和C基本相同,字符对应整数。

例:打印1-127对应的字符(ASCII码)。int i; char ch=(char)i;

转义字符:与C相同。 打印:/^_^\

布尔型:Java不用0和非0表示真假,用true和false,对应的数据类型:boolean

字符串:C++中一般用字符数组表示字符串,Java中用String类型表示字符串,更加方便,字符串和其他类型可以用+连接,编程更长的字符串。

1.3 基本运算

和C语法相同

算术运算:+、-、*、/、%

关系运算:>、<、>、=、<=、==、!=

逻辑运算:&&、||、!

赋值运算:+=、-=、*=、/=、++、—

逻辑短路:(A && B),和C语言相同

1.4 数组

1.4.1 定义方式

C语言:int a[5]; //5必须是常量

JAVA:int a[] = new int[5];int[] a = new int[5]; //标准写法,5可以是变量

int[] a; a=new int[4];第一句定义a为数组类型,第二句为数组分配内存。

定义直接赋值:int[] a = new int[]{1,2,3,4}

1.4.2 性质

C语言数组名只是个变量,存储了首地址的值,不是指针;(*a,a=a+5是错误的,*(a+5)可以)

JAVA中数组名虽然没有指针概念,但和指针性质相同。JAVA中的数组名性质和C中的指针p类似。

Java中数组使用里面的元素和C语言基本相同,如a[0]=5;

1.4.3 二维数组

C:int[3][4]

JAVA:int[][] a= new int[3][4]

int[][] d= new int[3][];

d[0]=new int[5]; d[1]=new int[100]; d[2]=new int[23];

1.4.4 for循环的数组元素遍历

int[] numbers = { 2, 3, 5, 7 };
//在for循环中,可以利用“变量类型 变量名称:数组名称”的形式,直接把数组元素赋值给该变量
for (int number : numbers) {
    System.out.println("number = " + number);
}

ch2.面向对象编程的基本概念

面向对象编程的英文为OOPObject Oriented Programming)。

2.1 类和对象

类是对象的抽象,对象是类的实例。

2.1.1 定义类和对象

JAVA里面定义类和C++基本相同,语法有小区别。

C++:

Customer zs; //C++实名对象

Customer *ls = new Customer(); //C++指针对象

JAVA:

Customer zs = new Customer();

类中的属性用变量表示,叫做成员变量,也叫做属性,域或字段(Field)。

类中的功能用函数表示,叫做成员函数,也叫做方法(Method)。

通过类定义一个对象,叫做实例化对象

2.1.2 成员函数的定义

语法和C基本相同,比C简单。

返回类型 函数名(参数列表) {

​ 函数体

}

对于一个程序来说,可能遇到的异常情况比较多,所以在if语句中先判断异常的情况,我们需要做很多事排除异常的情况,然后确定正常的情况。

【注意】

1.通过对象访问对象中的成员变量和成员函数(统称成员):用.号,类似C++中的->。

2.对象名的引用(指针)性质

Customer zs= new Customer();

zs类似于C++中的指针

zs与ls均为对象名;zs=ls,这里指的是ls的首地址给了zs,以后ls变化zs也变化。

image-20220616151245710

更多内容可以参考博客:

https://blog.csdn.net/GongchuangSu/article/details/51793544?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165536359916780366588914%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165536359916780366588914&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-51793544-null-null.142^v17^pc_search_result_control_group,157^v14^new_3&utm_term=java%E5%AF%B9%E8%B1%A1%E5%90%8D%E7%9A%84%E5%BC%95%E7%94%A8%E6%80%A7%E8%B4%A8&spm=1018.2226.3001.4187

class Test{
	void swap(Customer a,Customer b)  {  
		double t; t=a.balance; a.balance=b.balance; b.balance=t;	
	}
}
class Customer{
	double balance = 0;
}
class Pro1{
	public static void main (String[] args) {
		Customer zs = new Customer();  zs.balance = 1000;   
		Customer ls = new Customer();  ls.balance = 2000;   
		Test t = new Test();		t.swap(zs,ls);
		System.out.println(zs.balance);
	}	
}

输出为2000.

class Test{
	void swap(Customer a,Customer b)  {  
		Customer t; t=a; a=b; b=t;	
	}
}
class Customer{
	double balance = 0;
}
class Pro1{
	public static void main (String[] args) {
		Customer zs = new Customer();  zs.balance = 1000;   
		Customer ls = new Customer();  ls.balance = 2000;   
		Test t = new Test();		t.swap(zs,ls);
		System.out.println(zs.balance);
	}	
}

输出为1000.

2.1.3 构造函数(Constructor)

又名构造子,作用是对对象进行初始化。

总结:

(1)构造函数名称和类名相同,没有返回类型

(2)构造函数在对象被实例化的时候,自动调用1次,以后不可手动调用

(3)如果一个类中没有定义构造函数,系统自动给一个无参数的空的构造函数;若定义,则该无参数的空的构造函数失效;

(4)用this.成员名来确定该成员是类中成员变量而不是局部变量。

析构函数:对象消亡时调用,可以做一些资源释放的工作。

C++:~类名(){} 在Java中,不需要专门定义析构函数,其有垃圾收集机制;也可以用finalize函数代替:zs=null; System.gc();

class Customer{
   String account;
   double balance;
    Customer(String account,double balance)
    {
        init(account,balance);				//使用初始化函数
    }
    void init(String account,double balance)
    {
        this.account=account;
        this.balance=balance;
    }
}
class Pro1{
    public static void main(String[] args)
    {
        Customer zs = new Customer("张三",0);
        //此处省略1000行
        zs.init("张三",0);
    }
}

2.2 函数重载(overload)

2.2.1 重载条件

(1)参数个数不同;

(2)个数相同,类型不同;

(3)个数类型相同,顺序不同。

系统自动根据实参类型匹配相应的形参,调用相应函数。两个函数,名称、参数相同,返回类型不同,不能重载

重载实现了静态的多态性。(静态:虽然只有一个函数名,但是要编写多个函数)

2.2.2 练习

编写复数类,实现两个复数相加。

class ComplexNumber{
    int a,b;
    ComplexNumber(int a,int b)
    {
        this.a=a;
        
    }
    ComplexNumber add(ComplexNumber c)
    {
        return new ComplexNumber(a+c.a,b+c.b);
    }
    //ComplexNumber add(ComplexNumber c)
    //{
    //   this.a+=c.a;
    //   this.b+=c.b;
    //   return this;
    //}
    void display()
    {
        //……
    }
}
class Pro1{
    public static void main(String[] args)
    {
        ComplexNumber c1 = new ComplexNumber(5,6);
        ComplexNumber c2 = new ComplexNumber(7,8);
        ComplexNumber c3 = c1.add(c2);
        c3.display();
    }
}

2.3 静态变量和静态函数

2.3.1 静态变量

类中的成员变量,特性是被该类的所有对象共用

定义:变量前面加入static修饰符。

访问方式:对象名.变量名。由于它有共用性,一般用类名.变量名访问。(在C++中用类名::变量名表示)

​ 静态变量 普通变量

对象名. 可以 可以

类名. 可以 不可以

2.3.2 静态函数

在普通函数前增加static修饰符。

作用:一般情况下,完成公共的功能,或者访问静态变量。

  • 静态函数只能访问静态成员(静态变量/调用其他静态函数),以及自己函数内的局部变量(包含形参),不能访问类里面的非静态成员变量

  • 静态函数的访问方式和静态变量相同(类名.函数名)

    如前面复数相加,可以用ComplexNumber c3 = ComplexNumber.add(c1,c2);来实现。

class Customer{
	double balance;
	static String bankName;
	static void changeBankName(String s)  {  
		bankName = s;
		//balance = 1000;//错
	}
}

【练习1】给某个类实例化多个对象,每个对象赋1个编号,从1开始递增。

class Customer{
    int id;
    static int N=0;
    Customer(){N++;id=N;}
}
class Pro1{
    public static void main(String[] args)
    {
        Customer zs = new Customer();
        Customer ls = new Customer();
        System.out.println(zs.id);
    }
}

【练习2】软件登录之后,用户可能会打开很多不同界面,这些界面如何记住登录用户的用户名,以便载入相应内容?

需求:如何在不同的对象(界面)之间传递信息。

方法:将需要传递的信息定义为静态变量,一旦赋值,别的对象可以访问。

class LoginInfo
{
    static String account;	//要共享的变量
}
class Online
{
    //直接访问LoginInfo.account(无需传递)
}
class Chat
{
    //直接访问LoginInfo.account(无需传递)
}

​ 静态变量 普通变量

静态函数 可以 不可以

普通函数 可以 可以

ch3.面向对象的基本特征

3.1 封装(Encapsulation)

日常生活中的封装:隐藏细节、保证安全、便于使用。

软件中的封装有以下三种:

3.1.1 包级别的封装

多个类的管理,可以将类放入包中进行管理,和Windows文件夹类似。

【1】将类放入包中,可在类定义时,用package 包名;来确定。包名首字母一般小写;包名可以用.隔开,逻辑上分出包含关系。

package a.b; 表示a包里面的b子包。不可以直接通过新建文件夹和拷贝.class文件来建包,只能通过编译。

【2】同一个包中的类,可以直接使用。不在同一个包中的类,通过import 类的路径;来访问,并要确保被导入的类是public类。

注意:import 包名.*;可以表示导入该包中的所有类;import 包名.*;并不能导入其子包中的类。

3.1.2 类级别的封装

public类和默认权限的类

public类:在类的前面增加public修饰符 public class Customer{} 【注】public类所在的文件名一定要和类名相同

性质:能够被包外其他类导入并访问。

默认权限的类:class XXX{}

性质:只能被同一个包中其他类访问。

3.1.3 成员级别的封装

在成员变量或成员函数前加访问控制符,决定该成员的访问权限。

C++:private、protected、public

JAVA和C++类似:private/默认/protected/public

写法:在成员定义时加入访问控制符。举例:

private String account;		//private权限
String name;				//默认
protected String address;	//protected权限
public void query(){}		//public权限

private表示该成员只能在类的内部被访问;

默认表示该成员能被同一个包中的类访问;(private权限+同一个包中其他类访问)

protected:默认权限+包外子类访问;

public:包内、包外所有类访问。


编程过程中,一般情况下,成员变量定义为私有(被继承情况除外),成员函数定义为公有。

读取或给成员变量赋值:

采用public的成员函数解决。一般用get函数来读取,用set函数来赋值(setter和getter函数)。

public void setBalance(double balance)
{
    this.balance = balance;
}
public String getAccount(String account)
{
    return this.account;
}
public String[] getAddress()
{
    return this.address;
}

【练习】编写一个类,该类会被很多其他类调用,要确保该类对象在系统中使用的时候不超过1个实例。(单例模式

class TaskManager
{
    private static TaskManager tm = null;
    public static TaskManager getInstance()
    {
        if(tm == null)
        {
            tm = new TaskMannager();
		}
        return tm;
    }
    private TaskManager()
    {
        System.out.println("任务管理器实例化");
    }
    public void show()
    {
        System.out.println("任务管理器显示");
    }
}
class Desktop
{
	public void click()
    {
        TaskManager.getInstance.show();
    }
    public static void main(String[] args)
    {
        Desktop d = new Desktop(); d.click(); d.click();
    }
}

3.2 继承(Inheritance)

3.2.1 继承概述

C++:class 子类:[访问控制符] 父类{}

Java:class 子类 extends 父类{}

class Dialog{
    int w,h;
    public void show(){}
}
class FontDialog extends Dialog{
    String fontName;
}
class ParaDialog{
    int lineWidth;
}

注意:

(1)父类又叫做基类、超类,子类又叫派生类、扩展类,两者是组合关系;

(2)子类继承之后,父类中所有的非私有成员可以当成自己的使用;

(3)Java中不支持多重继承(同时继承好几个父类)(C++支持);

(4)子类中可以定义和父类相同的成员,调用时以子类定义的成员为准,这叫做覆盖或重写(override)(注意和overload(重载)区分)。重写不得使成员函数的访问权限更加严格,否则报错。重写可以对某些子类进行个性化。(重写的作用)

注:

重写时,子类定义成员和父类名称、返回类型、参数皆相同。

【练习】在网上下载了一个软件包,包含4个功能;其中,功能1直接使用,功能2在后面添加其他功能,功能3替换成自己的功能,功能4不能在本项目中使用。

class Module{
    public void fun1(){ System.out.println("功能1"); }
    public void fun2(){ System.out.println("功能2"); }
    public void fun3(){ System.out.println("功能3"); }
    public void fun4(){ System.out.println("功能4"); }
}
class MyModule extends Module{
    public void fun2(){ 
        super.fun2();	//调用父类中的fun2();
        System.out.println("其他功能");
    }
    public void fun3(){	System.out.println("自己功能");	}
    public void fun4(){}
    public static void main(String[] args)
    {
        MyModule mm = new MyModule();
        mm.fun1();
    }
}

3.2.2 继承的本质

3.2.2.1 父类构造函数的初始化

从现象上:

  • 子类实例化之前,系统为这个子类对象单独实例化一个相应的父类对象;

  • 每个子类构造函数调用之前,父类构造函数都会自动调用;

  • 如果父类构造函数有参数,必须通过在子类构造函数的第一句super(参数列表);给父类的构造函数赋予参数。

super和this相对应:

  • super.XX;表示调用父类的成员,this.XX表示调用本类中成员
  • super()表示调用父类构造函数;this(参数)表示调用自己的构造函数(用于构造函数互相调用的情况,比如如下代码)
class Customer{
    Customer(){
        this(3,5); 
    }
    Customer(int a,int b){}
}

3.2.2.2 本质理解

从本质上:子类和父类就是一种组合关系,只是底层用继承的编译器使得其使用起来更加方便。

继承的作用是代码重用

class FontDialog{
    Dialog d = new Dialog();
    FontDialog(){
        System.out.println("FontDialog构造函数");
    }
   	void fun(){d.w = 34;}
  	public static void main(String[] args)
    {
        new FontDialog(); new FontDialog();
    }
}

3.2.3 组合关系从强到弱

(1)最强:继承【血缘关系】

class Dialog{}
class FontDialog extends Dialog{}

父类中的非私有成员都可以在子类全范围类重用。

(2)次之:成员级别组合:A类定义为B类的成员(A和B的声明周期相同)【刘关张三结义】

class A{}
class B{
	A.a = new A();
	//其他代码
}

(3)次次之:函数参数级别的组合:A作为B中某个函数的参数,只在函数范围内有用【大学同学】

class A{}
class B{
	void fun(A a){/**/}
	//其他代码
}

(4)最弱:函数中使用【萍水相逢】

class A{}
class B{
	void fun()
	{
		/**/
		//使用A
	}
}

3.3 多态(Polymorphism)

一个东西,在不同场景下呈现出不同形态。

3.3.1 静态多态性:重载

重载的英文名是Overload,即一个函数名称,在不同实参传入情况下呈现出不同形态。

静态:一个函数名称,但还是要编写多个函数体。

3.3.2 动态多态性

理论基础:父类引用可以指向子类对象

性质:父类引用调用重写成员时,调用的是子类中的成员

推论:

对于以下两个类:

class Dialog{}

class FontDialog extends Dialog{}

有常见两种形式用于动态多态的操作:

  • ==函数定义父类形参,以子类对象作为实参传入==

定义:void fun(Dialog d){}

调用:fun(new FontDialog());

  • ==函数定义返回父类形参,以子类对象实际返回==

定义:

Dialog fun()

{

​ return new FontDialog();

}

调用:Dialog d = fun();

多态性的作用体现在:

  • 一个父类引用,在不同子类对象被指向的情况下,完成相应子类的功能。该思想是软件支持“不修改源代码的情况下二次开发”以及“不修改源代码的情况下,模块可拼装“的基础。

  • 将两个业务类的关系变远,叫做软件工程中的”低耦合“。

【例】编写一个类似于Word的软件,窗口上点击菜单,出现一个字体对话框。

​ 代码如下。对于外来类封装,可以在继承的子类中实例化外来类的对象,通过函数调用实现对外来类函数功能的使用,如MyNewDialog类中的函数语句段显示。

class FontDialog extends Dialog{
	public void show(){
		System.out.println("FontDialog显示");		
	}
}
class Dialog{
	public void show(){}
}
class Window{
	public void clickMenu(Dialog d){
		d.show();
	}
	public static void main (String[] args) {
		Window w = new Window();	w.clickMenu(new MyNewDialog());
	}
}
class MyNewDialog extends Dialog{
	MyFontDialog mfd = new MyFontDialog();
	public void show(){
		mfd.display();
	}
}
class MyFontDialog {
	public void display(){
		System.out.println("自己编写的漂亮的Dialog显示了!");
	}
}

3.4 抽象类与接口

3.4.1 抽象函数与抽象类

  • 在函数前面增加abstract关键字,称为抽象函数。该函数原则上要被重写,否则报错。
  • 含有抽象函数的类叫做抽象类,用abstract来修饰。即abstract class Dialog{}
  • 抽象函数必须被子类重写,除非该子类也是个抽象类
  • 抽象函数没有函数体(类似C++中的纯虚函数)。
  • 抽象类不能被实例化。

【注意】

  • 抽象类中可以有普通函数,则抽象类可以拥有一些自己的功能。抽象类可以写构造函数
  • 抽象类不能被实例化,但它的构造函数可以在子类中初始化某些变量,普通函数可以被子类重用;
  • 抽象类、抽象函数是一种标准化手段。
abstract class Dialog{	
	public Dialog(){ System.out.println("Hello"); }
	public abstract void show();		
	public void fun(){}			
}
class FontDialog extends Dialog{
	public void show(){	System.out.println("FontDialog显示");	}
}

3.4.2 接口

  • 比抽象类更加“抽象”的“类“,叫做接口(interface)

  • 接口和抽象类类似,只不过它里面所有的函数只能是抽象函数(除非子类为抽象类,否则必须被重写)。其中的abstract可以省略。

  • 接口中任何变量==只能是常量==不能赋值;也即:接口中所有变量都是public的、静态的、final的变量(常量)。
  • 接口中所有的成员(变量/函数)都是public的,其中关键字public可以省略(默认public)。但子类重写函数时不能省略
  • 一个类最多只能继承一个抽象类,但是可以实现(implements)多个接口(子类也叫做实现类),用逗号隔开。也可以在继承一个父类的同时,实现一个或多个接口,此时extends关键字必须位于implements关键字之前。即class 子类 extends 父类 implements 接口1,接口2,…{}
  • 接口起到连接作用,有时也叫“界面”。
interface Dialog{	
	int a=5;//常量
	void show();	
}

class FontDialog implements Dialog{
	public void show(){		
		System.out.println(Dialog.a); 
		//Dialog.a = 5;	 错误
		System.out.println("FontDialog显示");		
	}
}

class Window{
	public void clickMenu(Dialog d){
		     d.show();
	}
	public static void main (String[] args) {
		Window w = new Window();	w.clickMenu(new FontDialog());
	}
}

3.5 其他内容

3.5.1 final关键字

在类、成员变量、成员函数前增加final关键字。

  • 类前面增加final,该类不能有子类。
  • 成员函数前增加final,该函数不能被重写。
  • 成员变量前增加final,该变量是常量,必须事先赋值不能再次赋值。(类似C语言的const)

3.5.2 Object类

​ 在Java中定义一个类时,如果没有用extends明确标明直接父类,那么该类默认继承Object类,因此,Object类是所有类的最高父类,或者说,Java中任何一个类都是Object的子类。

​ 该类有两个作用:

  • Object类中,有一个toString方法,可以给子类一个以字符串展示的机会。

    public class Customer {
        private String account;
        private String cname;
        private double balance;
        public Customer(String name){
            this.name = name;
        }
        public String toString(){
        	return account+"-"+cname+"-"+balance;
        }
        public static void main(String[] args){
        	Customer zs = new Customer("张三");
        	System.out.println(zs);
        }
    }

    打印结果为一个字符串。

  • Object类中,有一个equals方法,可以给子类对象一个比较它们是否相等的机会。

    两个对象名,a==b除非a与b指向同一个对象,才返回true;如果要定义两个对象内容相等,就需要重写”equals();“。

    public class Customer {
        private String name;
        private double balance;
        public Customer(String name){
        	this.name = name;
        }
        public boolean equals(Customer cus){
        	if(name.equals(cus.name) && balance == cus.balance){  
                //字符串相等使用equals方法
        		return true;
        }
        	return false;
        }
        public static void main(String[] args){
        	Customer cus1 = new Customer("张三");
        	Customer cus2 = new Customer("张三");
        	System.out.println(cus1.equals(cus2));
        }
    }

【练习】详细讲解多态性的应用。

编写一个按钮,具有点击功能;编写一个窗口,上面有个按钮。点击按钮,可以让窗口变红或变蓝。以上写模拟代码即可。

class Button{
	ButtonListener bl;
	public void bind(ButtonListener bl){
        //实现绑定
        this.bl = bl; 
    }
	public void click(){
		bl.action(this);
	}
}
interface ButtonListener{
	public void action(Button b);
}
class Window implements ButtonListener{
	Button b1 = new Button();
	Button b2 = new Button();
	public Window(){      b1.bind(this);    b2.bind(this);  }
	public void action(Button b){
		if(b==b1)  
            this.toRed();
		else 
            this.toBlue();
	}
	
	public void toRed(){
		System.out.println("窗口变红");
	}
	public void toBlue(){
		System.out.println("窗口变蓝");
	}
	public static void main (String[] args) {
		Window w = new Window();  w.b2.click();
	}
}

ch4.Java语言核心包

4.1 java.lang包

java.lang包,是Java中最基础的核心包,里面包含的是最基础的类。如数学运算、字符串处理等。这个包在默认情况下,系统将其导入,不需要用import导入到程序中就可以直接使用里面的类。

4.1.1 java.lang.Math类

负责数学运算,下面展开介绍随机数相关问题:

  • 数组放置在1-100的数字,要求随机打乱:可以通过1-100按顺序,随机找两个打乱,打乱多次
class MathTest{
	public static void main (String[] args) {
		int[] arr = new int[100];
		for(int i=0;i<100;i++){
			arr[i] = i+1;
		}
		for(int i=0;i<10000;i++){
			int temp;
			int L1 = (int)(Math.random()*100);
			int L2 = (int)(Math.random()*100);
			temp = arr[L1]; arr[L1] = arr[L2];  arr[L2] = temp;
		}
		for(int i=0;i<100;i++){
			System.out.print(arr[i] + " ");
		}
	}
}
  • 公平的赌博游戏,久赌必输

    100元筹码,随机数,0-0.5之间,赢10元,0.5-1之间,输10元。赢满500走人,其中输光退场。

class MathTest{
	public static void main (String[] args) {
		int M = 100;
		while(true){
			double d = Math.random();
			if(d<0.5)   
                M += 10;
			else
                M -= 10;
			System.out.println("当前财产:"  + M);
			if(M==0)    {  System.out.println("输光走人");  break; }
			if(M==500)  {  System.out.println("赢满走人");  break; }
		}
	}
}

4.1.2 java.lang.String类

负责字符串处理

1.初始化字符串,有两种方式:

  • 直接赋值 String s = "China";

    注意:直接赋值,系统为了节省内存,用的是“池机制”,即:String s1 = "China"; String s2 = "China"; 打印s1==s2,输出为true,但若两个字符串赋值方式不同,用”==“判断两个字符串是否相等,结果将存在问题:

    String str1 = "aa";
    String str2 = new String("aa");
    System.out.println(str1==str2);				//false
    System.out.println(str1.equals(str2)); 	 	//true

​ 因此,要判断两个字符串是否相等,应使用s1.equals(s2)

  • 创建字符串对象 String s = new String("China");

2.charAt函数:s.charAt(1),返回h

3.contains函数(boolean类型),若包含则返回true,否则为false

4.indexof函数,返回第一次出现的index(位置)

5.replace函数,返回替换后的结果(返回的是新的字符串,但原先字符串的内容未变化

4.1.3 java.lang.StringBuffer类

负责字符串处理。StringBuffer类是自变型字符串操作之后,自己变化。如:执行语句:

StringBuffer sm = new StringBuffer("China-Hunan"); sm.append("-CSU"); ,sm变化。

而String是非自变型字符串,在字符串频繁操作场合,大大消耗内存。如:sm如果是String,执行语句sm.replace("CSU","中南大学");,sm没变。

4.2 基本数据类型的包装类

数据类型 包装类

int Int

float Float

double Double

重要作用:将字符串转成相应的数值

//因为Integer.parseInt是静态函数,直接对类名.函数名进行操作即可,无需实例化对象。
int i = Integer.parseInt("123");
float f = Float.parseFloat("3.14");	
double d = Double.parseDouble("3.1415926");

若将数值转成字符串,统一用String.valueOf(各类数值);

4.3 java.util包

java.util是工具包,其中最重要的是Java集合框架(变长数组),共三类:List类、Set类、Map类。

4.3.1 List类

一维变长数组,元素有位置信息,可重复。子类有:

ArrayList:底层用数组存储

LinkedList:底层用链表存储

Vector:底层用数组存储,实现了多线程的安全

import java.util.*;
class UtilTest{
	public static void main (String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("北京");
        list.add(2,"巴黎");
        System.out.println(list.get(3));
        for(String s:list)	//遍历
        {
            System.out.println(s);
        }					//或使用for(int i = 0; i<list.size(); i++)
	}
}
  • 用法完全相同(增删改查)

  • 一边插入一边排序:插入排序法

  • 找位置一般用二分法找位置

4.3.2 Set类

一维变长数组,元素无位置信息,==不可重复==。常用的有:HashSet(元素乱序)、LinkedHashSet(元素保序)。

4.3.3 Map类

二维变长数组,是key和value的对应。常用的有:HashMap、LinkedHashMap

key value

姓名 张三

性别 男

籍贯 湖南长沙

key不可重复,value可重复。若两个不同的对象指定同一个key值,后面的将会把前面的覆盖。

作业:输入一个长字符串,显示里面每个字符出现的次数。

import java.util.*;
class UtilTest{
	public static void main (String[] args) {
        String str = "qqqwwweeeerr111我爱爱爱爱爱你";
        Map<Character, Integer> map = new HashMap<>(); //定义Map集合
        for (int i = 0; i < str.length(); i++) {
            int count = 0; //初始化count的值
            for (int j = 0; j < str.length(); j++) { // 遍历一次str字符串
                if (str.charAt(i) == str.charAt(j)) {//判断字符串i下标和j下标的字符是否相等,若相等就让count自增
                    count++;
                }
            }
            map.put(str.charAt(i), count);	//把下标为i的字符和他的出现次数存进去
        }
        System.out.println(map);  //输出Map集合
    }
}

LinkedHashMap可以确保是按照添加进去的顺序进行存储。

ch5.Java异常处理

5.1 异常的定义

异常【Exception(例外)】:程序在运行过程中出现的不正常现象。

  • 不正常:(1)编译阶段,程序报错-修改代码;(2)运行阶段,程序报错-处理异常。
  • 异常若不进行处理,危害在:(1)程序异常停止;(2)得不到友好的提示。

5.2 异常处理的重要关键词

5.2.1 关键词try、catch

异常处理三大关键词 try - catch - finally

​ 个数 1 1…n 0/1

​ 重要性 *** ** *

  • try:将可能出现异常的代码放在try中;
  • catch:将异常出现后,将需要处理的代码放在catch中。即try{}catch(Exception e){}

【细节问题】

  • try中的代码是业务代码,如果没有问题,不执行catch;如果有异常,略过try中剩余的代码,转而执行catch,执行完毕后,程序继续向下执行,不会终止。

  • 一个try后面至少跟一个catch,可以跟多个catch,用来处理不同的异常。

  • catch(Exception e)即将异常问题一网打尽,该语句若跟别的异常catch并列,应放到最后。

5.2.2 Java常见异常分类

  • NumberFormatException:数值格式异常

  • ArithmeticException:数学计算异常(如除数为0)

  • ArrayIndexOutOfBoundsException:数组越界异常
  • NullPointerException:对象未分配内存异常(如String str = null)

5.2.3 拓展应用

异常处理有时候可以解决一些有趣的问题。如:输入一个整数,打印这个数的平方;如果输入格式不对,反复出现输入框,直到输入格式正确,打印平方。

class ExceptionTest{
	public static void main (String[] args) {
		while(true){
			try{
				String str = javax.swing.JOptionPane.showInputDialog("输入整数");
				int N = Integer.parseInt(str);
				int R = N * N;
				System.out.println("结果是:" + R);
				break;		
			}catch(Exception e){}
		}
	}
}

5.2.4 关键词finally

和try、catch一起使用的还有一个关键词:finally。

  • finally代码块原理:finally加在catch后面,表示不管是否出现异常,都需要执行的代码。try-catch之后,可以有finally,也可以没有finally。如打开文件、处理文件、关闭文件(finally)。finally块确保了程序的安全性。

    class ExceptionTest{
    	public static void main (String[] args) {
    		try{
    			System.out.println("打开文件");
    			System.out.println("处理文件");  // double a = 10/0;
    		}catch(Exception e){  System.out.println("以上代码出现了异常"); }
    		finally{  System.out.println("关闭文件");  }
    	}
    }
  • finally块不是可有可无的,只要有try,不管try里面发生了循环跳出、函数返回等,配套的finally一定会执行,从底层确保了需要执行的代码一定会执行(安全性)

    class ExceptionTest{
    	public static void main (String[] args) {
    		for(int i=1;i<=10;i++){
    			try{
    				System.out.println("AAAAAAA");	
    				if(i==1)  break;
    			}catch(Exception e){ System.out.println("BBBBB");	 }
    			finally { System.out.println("CCCCCCC"); }
    		}
    	}
    }

5.3 异常抛出关键词throw、throws

  • 异常处理另外两个关键词:throw、throws抛出

  • 异常的原理:try{…}catch(Exception e){…}
    原理:try里面的代码,如果出现异常,系统底层将其包装成一个对象,抛出。被catch块捕获赋值给参数e,并执行catch块。

【例】编写一个函数“setAge”,输入一个人的年龄整数,如果在0-100之间,返回年龄本身数值;否则返回“年龄范围错误”。

class ExceptionTest{
	public int setAge(int age) throws Exception{
		if(age<0 || age>100) {
		    Exception e = new Exception("年龄范围错误");
		    throw e;
		}
		return age;
	}	
	public static void main (String[] args) {
		ExceptionTest et = new ExceptionTest();
		try{ 
			int age = et.setAge(1000);
		}catch(Exception ex){  System.out.println(ex.getMessage()); }
	}
}
  • throw表示在函数中抛出一个实际的异常对象,throws表示在函数定义上标记该函数可能抛出异常。

  • 标记了throws的函数,在调用时,(1)原则上必须用try包围,并编写catch代码处理可能出现的异常(还有非原则情况,即原来的函数抛出的异常类型是RuntimeException的子类),这叫做就地捕获;(2)或者将异常再次向前抛出

  • throws和throw一般用于自定义异常信息并要求从函数中返回的情况。为了不影响函数的正常返回,将自定义异常的信息用异常包装,throw抛出,函数用throws标记。
class ExceptionTest{
	public int setAge(int age) throws Exception{  //异常的向前抛出
		if(age<0 || age>100) {
		    Exception e = new Exception("年龄范围错误");
		    throw e;
		}
		return age;
	}
	public void test () throws Exception{  		//异常的向前抛出
		this.setAge(1000);		
	}
	public static void main (String[] args) {		
		ExceptionTest et = new ExceptionTest();
		try{et.test();} 				   	//异常的就地捕获
		catch(Exception ex){ System.out.println(ex.getMessage());}
	}
}
  • 异常抛出与异常处理相比,异常处理使用场合更多。

ch6.Java多线程开发

6.1 进程与线程的概念

进程(Process):操作系统中能够“同时”运行的多个应用程序(QQ、浏览器、Word、WPS)。

线程(Thread):一个应用程序中,能够“同时”运行的多个任务,比如在线播放(一边下载,一边播放),很多软件都支持线程功能,如QQ可以一边接收信息、一边用户可以发送信息,抖音可以一边下载、一边播放。

两者实际上在底层是“分时”(时间片轮转),由操作系统决定。

针对Java语言,我们讲解线程。

6.2 实现线程的两种方法

6.2.1 方法一

  • 让需要实现线程的类,继承java.lang.Thread类;
  • 将需要实现线程的代码,放在从Thread类中重写的run函数里面;
  • 开启线程,用线程对象的start方法。
class Downloader extends Thread{  		//①第一步:继承Thread
	public void run() {					//②第二步:重写run函数
		for(int i=1;i<=10;i++){
			try{
                Thread.sleep(1000);  	//休息1秒
            } catch(Exception ex){}  
			System.out.println("下载进度:"+i*10+"%");
		}		
	}
}
class Player extends Thread{
	public void run(){
		for(int i=1;i<=10;i++){
			try{  Thread.sleep(1000);  } catch(Exception ex){}  
			System.out.println("播放进度:"+i*10+"%");
		}	
	}
}
class ThreadTest{
	public static void main (String[] args) {
		Downloader d = new Downloader();
		Player p = new Player();
		d.start(); 	   p.start();  	 //③第三步:用start方法开启各个线程的run函数
	}
}

当要在执行某个线程一段时间后执行另一个进程时,可用以下代码:

class Downloader extends Thread{ 	    //①第一步:继承Thread
	public void run() {					//②第二步:重写run函数
		for(int i=1;i<=10;i++){
			try{  Thread.sleep(1000);  } catch(Exception ex){}  
			System.out.println("下载进度:"+i*10+"%");
			if(i==2){  					//一个线程调用另一个线程
				new Player().start();
			}
		}		
	}
}
class Player extends Thread{
	public void run(){
		for(int i=1;i<=10;i++){
			try{  Thread.sleep(1000);  } catch(Exception ex){}  
			System.out.println("播放进度:"+i*10+"%");
		}	
	}
}
class ThreadTest{
	public static void main (String[] args) {
		Downloader d = new Downloader();		
		d.start();		//③第三步:用start方法开启各个线程的run函数
	}
}

6.2.2 方法二

  • 让需要实现线程的类,继承java.lang.Runnable接口;
  • 将需要实现线程的代码,放在从Runnable接口中重写的run函数里面;
  • 实例化线程,将类的对象传入线程的构造函数,再调用线程的start方法。
class Downloader implements Runnable{ 	 //①第一步:实现Runnable
	public void run() {					 //②第二步:重写run函数
		for(int i=1;i<=10;i++){
			try{  Thread.sleep(1000);  } catch(Exception ex){}  
			System.out.println("下载进度:"+i*10+"%");
		}		
	}
}
class Player implements Runnable{
	public void run(){
		for(int i=1;i<=10;i++){
			try{  Thread.sleep(1000);  } catch(Exception ex){}  
			System.out.println("播放进度:"+i*10+"%");
		}	
	}
}
class ThreadTest{
	public static void main (String[] args) {
		Downloader d = new Downloader();      Thread th1 = new Thread(d);
		Player p = new Player();     		  Thread th2 = new Thread(p);		
		th1.start();	th2.start();     //③第三步:实例化线程,将类的对象传入线程的构造函数,再调用线程的start方法
	}
}

用了多线程,多个任务完成得比以前快,不是因为多线程让CPU运行更快,是让CPU的利用率提高。

6.3 线程控制

  • 线程开启(start)
  • 线程暂停(suspend)
  • 线程继续(resume)
  • 线程结束(run函数运行完毕)

【例】下载文件,下载到30%,暂停下载5秒,5秒后继续下载,编写代码实现该功能。

class Downloader extends Thread{ 
	public void run() {	
		for(int i=1;i<=10;i++){
			try{  Thread.sleep(1000);  } catch(Exception ex){}  
			System.out.println("下载进度:"+i*10+"%");
		}		
	}
}
class ThreadTest{
	public static void main (String[] args) throws Exception {
		Downloader d = new Downloader();
		d.start();	
		Thread.sleep(3200);
		d.suspend();
		Thread.sleep(5000);
		d.resume();
	}
}

【注意】suspend、resume是不建议使用的函数,有死锁倾向

这两个函数,特别是suspend,在暂停时不会释放线程中的资源,导致资源被该线程长期持有,别人不能使用,故可能造成循环等待。所以不建议使用。可采用以下方法:线程暂停,就让该线程结束(run函数运行完毕);线程继续,新开启一个线程(start)。

class Downloader extends Thread{ 
    boolean RUN = true;
    static int i = 1;
	public void run() {	
		for(;i<=10&&RUN;i++){
			try{  Thread.sleep(1000);  } catch(Exception ex){}  
			System.out.println("下载进度:"+i*10+"%");
		}		
	}
}
class ThreadTest{
	public static void main (String[] args) throws Exception {
		Downloader d = new Downloader();
		d.start();	
		Thread.sleep(3000);
		d.RUN = false;
		Thread.sleep(5000);
		d = new Downloader();	 	//新开启一个线程继续
		d.start();
	}
}

6.4 线程同步

  • 主要出现在“多个线程访问同一内存资源的场合”,如:下载文件时,如果同时开多个线程下载文件,可以提高下载速度
  • 解决同步问题:用synchronized原子代码包含起来,保证其要么全部运行,要么全部不运行,别的线程在此之间,无法抢占CPU
    这种方式。实际上,是将线程功能进行了退化,“几乎又变成串行”。
//此处不能用方法1(继承thread类),定义d1、d2对象,因为synchronized的参数this打在的是对象中(synchronized运行结束this上的标记自动消亡),	这种方法会使this标记打在了两个对象,而Runnable方法可使this打在一个对象,达到目的。
class Downloader implements Runnable{ 
    static int i = 1;				//定义静态变量,可以使类中所有对象共用
	public void run() {	
		while(true){
			synchronized(this){
				if(i>10)  {	break; }
				try{  Thread.sleep(1000);  } catch(Exception ex){}  
				System.out.println("下载进度:"+i*10+"%");
				i++;
			}			
		}		
	}
}
class ThreadTest{
	public static void main (String[] args) throws Exception {
		Downloader d = new Downloader();
		new Thread(d).start();  new Thread(d).start();
	}
}
  • 实际过程中,解决同步问题的方法是:人为进行资源划分(如一个线程下载前50%,另一个线程下载后50%)。

ch7.JavaIO操作

IO:输入输出。

输入设备:键盘、鼠标、扫描仪

输出设备:打印机、显示器

同时属于输入输出设备:硬盘

输入设备和输出设备是站在内存的角度划分的。

将文件从硬盘上读入,QQ收到对方信息;将文件保存到硬盘,QQ输出信息给对方。

这里重点讲解文件输入输出,对应的包是java.io。

7.1 封装文件

java.io.File:文件封装,得到文件信息,删除文件,重命名文件

[例1]用File类封装E:/test.txt.

import java.io.*;
class IOTest{
	public static void main (String[] args) {
		File f = new File("E:/test.txt");
		f.delete();
	}
}

[例2]列出D盘下面所有的文件,并显示.

import java.io.*;
class IOTest{
	public static void main (String[] args) {
		File f = new File("D:/");
		File[] files = f.listFiles();
		for(File file : files){
			System.out.println(file.getPath() + ":" + file.length());
		}
	}
}

[例3]删除E:/testDir下面的所有文件.

import java.io.*;
class IOTest{
	public static void main (String[] args) {
		File f = new File("D:/testDir");
		File[] files = f.listFiles();
		for(File file : files){
			file.delete();
		}
	}
}

[例4]编写一个病毒,用户运行,自动清空输入文件夹路径中所有的文件和文件夹。

package VirusTest;
import java.io.File;
public class VirusTest {
    public static void FileDelete(File f) {
        if(f.isDirectory())
        {
            File[] files = f.listFiles();
            for (File file : files) {
                FileDelete(file);
            }
        }
        f.delete();
    }
    public static void main(String[] args) throws Exception{
        String str = javax.swing.JOptionPane.showInputDialog("请输入您要删除的文件夹");
        File f = new File(str);
        FileDelete(f);
    }
}

7.2 读文件

场景:test.txt里面有一些内容,读入到内存后,显示在屏幕上。

7.2.1 java.io.FileInputStream

FileInputStream类一个个字节读取,对中文支持不佳。文件输入输出流,打开之后,记得及时关闭

【注】字节流和字符流

字节流以字节的形式读取文件,对于有一些“双字节字符”,支持不好。为了解决该问题,Java中推出专门支持字符流的文件输入输出类。

import java.io.*;
class IOTest{
	public static void main (String[] args) throws Exception{
		File f = new File("test.txt");
		FileInputStream fis = new FileInputStream(f);
		while(true){
			int i = fis.read();
			if(i==-1) break;
			char ch = (char)i;
			System.out.print(ch);
		}
		fis.close();
	}
}

批量读取

import java.io.*;
class IOTest{
	public static void main (String[] args)  throws Exception{
		File f = new File("test.txt");
		FileInputStream fis = new FileInputStream(f);
		byte[] data = new byte[(int)f.length()];
		fis.read(data);
		String s = new String(data);
		System.out.println(s);		
		fis.close();
	}
}

7.2.2 java.io.FileReader

字符读取,可一个个字符读,也可一块块读,支持字符。

7.2.3 java.io.BufferedReader

重点针对字符特别是多字节字符讲解的不是FileReader,最常见使用的是“按行读”的另一个类:java.io.BufferedReader。如果对字符串进行读取,最佳办法是用:BufferedReader。它支持按行读字符串,和FileReader配套

import java.io.*;
class IOTest{
	public static void main (String[] args)  throws Exception{
		File f = new File("test.txt");
		FileReader fr = new FileReader(f);
		BufferedReader br = new BufferedReader(fr);
		while(true){
			String s = br.readLine();
			if(s==null) break;
			System.out.println(s);
		}
		fr.close();
	}
}

字节流读取(FileInputStream)和字符流读取(FileReader/BufferedReader)进行比较,FileInputStream(适合图像、视频、语音、文本都支持),而字符流读取一般只适合字符和字符串。

【总结】

  • java.io.FileInputStream:字节读取,可一个个字节读,也可一块块读,使用最广
  • java.io.FileReader:字符读取,可一个个字符读,也可一块块读,支持字符
  • java.io.BufferedReader:支持按行读字符串,和FileReader配套

7.3 写文件

场景:定义一个变量,保存到test.txt中。

  • java.io.FileOutputStream:字节流输出

注意:向文件保存数据时,有两种模式:1)在文件末尾添加; 2)覆盖原有内容,用append参数确定。

import java.io.*;
class IOTest{
	public static void main (String[] args)  throws Exception{
		File f = new File("test.txt");
		FileOutputStream fos = new FileOutputStream(f, false);	//append参数为false,覆盖原有内容
		String str = "CSU中南大学";
		fos.write(str.getBytes());	//getBytes函数转为字节数组,然后写入文件
		fos.close();
	}
}
  • java.io.FileWriter:字符流输出
  • java.io.PrintStream:支持丰富的格式对字符串进行输出,字符串万能输出,和FileOutputStream配套

[例1]将一个99乘法表保存到test.txt中。

import java.io.*;
class IOTest{
	public static void main (String[] args) throws Exception{
		File f = new File("test.txt");
		FileOutputStream fos = new FileOutputStream(f, false);
		PrintStream ps = new PrintStream(fos);
		for(int i=1;i<=9;i++){
			for(int j=1;j<=i;j++){
				ps.print(i + "*" + j + "=" + i*j + " ");
			}
			ps.println();
		}		
		fos.close();
	}
}

[例2]将一个图像加密,加密方法:每个字节的值+3。

import java.io.*;
class IOTest{
	public static void main (String[] args)  throws Exception{
		File f1 = new File("img1.jpg");
		File f2 = new File("img2.jpg");
		FileInputStream fis = new FileInputStream(f1);
		FileOutputStream fos = new FileOutputStream(f2);		
		while(true){
			int i = fis.read();
			if(i==-1) break;
			i+=3;
			fos.write(i);
		}
		fos.close();
		fis.close();
	}
}

另外,RandomAccessFile类可以提供文件的随机访问,既支持文件读,又支持文件写。

ch8.Java界面开发与事件处理

本章讲述Java开发桌面应用程序,包括用Swing开发GUI程序、Java事件处理与Java绘图。

Swing:Java中的一个包,负责开发GUI程序

GUI:图形用户界面,一般指可视化桌面系统中的应用程序。

Windows:将应用程序从字符界面拓展到图形界面。

常见图形用户界面的基本结构

  • 界面(容器窗口)

  • 控件(界面上的组件,如按钮、文本框等)

  • 渲染(颜色、字体等)
  • 事件响应(完成功能)

界面控件、渲染中的图标使用的包:javax.swing包【注:javax的x:eXtension(扩展)】

渲染(颜色:Color、字体:Font)使用的包:java.awt包。

8.1 界面

又称容器、窗口,提供整个界面的空间。

  • 框架界面: javax.swing.JFrame
  • 窗口界面: javax.swing.JWindow
  • 对话框界面: javax.swing.JDialog

一般掌握第(1)种即可。

显示:setVisible函数

[例]在桌面出现一个界面,标题是:HelloWorld

用框架界面

import javax.swing.JFrame;
class GUI1{
	public static void main(String[] args) throws Exception  {
		JFrame jfm = new JFrame("HelloWorld");
		jfm.setSize(600,400);
		jfm.setLocation(300,200);
		jfm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);		//退出框架界面
		jfm.setVisible(true);
	}
}

通过构造函数进行初始化

import javax.swing.JFrame;
class GUI extends JFrame{
	public GUI(){
		super("HelloWorld");		//调用父类的构造函数
		this.setSize(600,400);
		this.setLocation(300,200);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args) throws Exception  {
		new GUI();
	}
}

8.2 控件

界面上的组件,如按钮、文本框、复选框等。

注意:Java中,容器还有:面板 javax.swing.JPanel

一般我们在界面上添加一个面板,面板上增加控件

  • 按钮:javax.swing.JButton
  • 静态文本:javax.swing.JLabel
  • 文本框:javax.swing.JTextField
  • 密码框:javax.swing.JPasswordField
  • 多行文本框:javax.swing.JTextArea
  • 下拉菜单:javax.swing.JComboBox
  • 复选框:javax.swing.JCheckBox

举一反三:

  • 知道一个控件,用哪个类?
  • 查文档看构造函数和其他重要函数
  • 将控件实例化后加到容器中
import javax.swing.*;
class GUI extends JFrame{
	private JLabel jlb = new JLabel("——————欢迎来到中南大学社团学生注册系统——————");	
	private JButton jbt = new JButton("学生注册");
	private JLabel jlbAcc = new JLabel("请您输入账号");
	private JTextField jtfAcc = new JTextField(20);
	private JLabel jlbAdd = new JLabel("请您选择您的家乡");
	private JComboBox jcbAdd = new JComboBox();	
	private JCheckBox jcbMember = new JCheckBox("是否立即成为会员",true);	
	private JLabel jlbInfo = new JLabel("请您输入个人详细描述");
	private JTextArea jtaInfo = new JTextArea(20,30);
	private JPanel  jpl = new JPanel();
	public GUI(){
		jpl.add(jlb);		jpl.add(jbt);	jpl.add(jlbAcc);  jpl.add(jtfAcc);
		jpl.add(jlbAdd);  jpl.add(jcbAdd);  jpl.add(jlbInfo);  jpl.add(jtaInfo);
		jcbAdd.addItem("湖南");	jcbAdd.addItem("湖北");
		jcbAdd.addItem("河南");	jcbAdd.addItem("河北");
		jpl.add(jcbMember);
		
		this.add(jpl);		
		this.setSize(400,600);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args) throws Exception  {
		new GUI();
	}
}

8.3 渲染

  • 包含颜色、字体(java.awt)、图标(javax.swing)三部分

  • 任何界面和控件都可以设置背景颜色和前景颜色

    setBackground(颜色) setForeground(颜色) (文档可查)

​ 凡是有字的控件,都有setFont函数(文档可查)

  • 颜色:java.awt.Color

  • 字体:java.awt.Font

    import javax.swing.*;
    import java.awt.*;
    class GUI extends JFrame{
    	private JButton jbt = new JButton("学生注册");
    	private JPanel  jpl = new JPanel();
    	public GUI(){
    		jpl.setBackground(new Color(0,255,255));
    		jbt.setBackground(Color.blue);
    		jbt.setForeground(Color.yellow);
    		Font f = new Font("微软雅黑",Font.BOLD,50);
    		jbt.setFont(f);
    		jpl.add(jbt);
    		this.add(jpl);		
    		this.setSize(400,600);
    		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE );
    		this.setVisible(true);
    	}
    	public static void main(String[] args) throws Exception  {
    		new GUI();
    	}
    }
  • 图标:很多控件都可以设置图标,如按钮、静态文本等。但不是所有,比如文本框就不能。具体看文档。用的函数:setIcon函数

    注意:Icon和ImageIcon在swing中

    主要方法有:

    控件.setBackground和setForeground(颜色)

    控件.setFont(字体)

    控件.setIcon(图标)

    【例】将按钮设置为图标形式。

    import javax.swing.*;
    import java.awt.*;
    class GUI extends JFrame{
    	private JButton jbt = new JButton();
    	private JPanel  jpl = new JPanel();
    	public GUI(){
    		ImageIcon icon = new ImageIcon("img.jpg");
    		jbt.setIcon(icon);
    		jbt.setText("这是一个按钮");
    		jpl.add(jbt);
    		this.add(jpl);		
    		this.setSize(600,800);
    		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE );
    		this.setVisible(true);
    	}
    	public static void main(String[] args) throws Exception  {
    		new GUI();
    	}
    }

8.4 界面布局管理

布局:将控件有序的放在界面上,使用java.awt包。

8.4.1 FlowLayout

JPanel默认的布局方式是:流式布局(FlowLayout),优先放在1行,放不下,到后面1行。该布局方式由java.awt.FlowLayout来管理;
任何容器管理类都有setLayout函数设置布局。

[例]将一个按钮,一个文本框放在界面上。

import javax.swing.*;
import java.awt.*;
class Layout1 extends JFrame{
	private JTextField jtf = new JTextField(20);
	private JButton jbt = new JButton("按钮");
	private JPanel jpl = new JPanel();	
	public Layout1(){
		FlowLayout fl = new FlowLayout(FlowLayout.LEFT,20,20);
		jpl.setLayout(fl);
		this.add(jpl);	jpl.add(jtf);		jpl.add(jbt);
		this.setSize(300,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args) throws Exception  {
		new Layout1();
	}
}

8.4.2 GridLayout

网格布局,将界面设置为多行多列的格子,放置控件。该布局方式由java.awt.GridLayout来管理。

[例1]放置24个按钮在界面上。

import javax.swing.*;
import java.awt.*;
class Layout1 extends JFrame{
	private JPanel jpl = new JPanel();	
	public Layout1(){
		GridLayout gl = new GridLayout(6,4); 
		jpl.setLayout(gl);
		this.add(jpl);	
		for(int i=1;i<=24;i++){
			jpl.add(new JButton(String.valueOf(i)));
		}				
		
		this.setSize(300,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args) throws Exception  {
		new Layout1();
	}
}

[例2]制作一个8*8国际象棋棋盘。

import javax.swing.*;
import java.awt.*;
class Layout1 extends JFrame{
	private JPanel jpl = new JPanel();	
	public Layout1(){
		GridLayout gl = new GridLayout(8,8); 
		jpl.setLayout(gl);
		this.add(jpl);	
		for(int i=1;i<=8;i++){
			for(int j=1;j<=8;j++){
				JPanel pl = new JPanel();
				if((i+j)%2==0)  {pl.setBackground(Color.white);}
				else { pl.setBackground(Color.black);}
				jpl.add(pl);
			}
		}				
		
		this.setSize(300,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args) throws Exception  {
		new Layout1();
	}
}

8.4.3 BorderLayout

边界布局:将界面分为东西南北中,添加控件。该布局方式由java.awt.BorderLayout来管理。

JFrame的默认布局方式是BorderLayout。

image-20220419112326206

import javax.swing.*;
import java.awt.*;
class Layout1 extends JFrame{
	private JPanel jpl = new JPanel();	
	public Layout1(){
		BorderLayout bl = new BorderLayout(); 
		jpl.setLayout(bl);
		this.add(jpl);	
		jpl.add(new JButton("按钮"),BorderLayout.SOUTH);
		jpl.add(new JTextField(),BorderLayout.NORTH);
		jpl.add(new JTextArea(),BorderLayout.CENTER);
		jpl.add(new JButton("按钮"),BorderLayout.WEST);
		jpl.add(new JButton("按钮"),BorderLayout.EAST);
		this.setSize(300,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args) throws Exception  {
		new Layout1();
	}
}

[注]教科书中有关于计算器的编制程序。

8.4.4 完全自由布局

在完全自由布局中,控件的位置,大小,用坐标决定,而不是由界面大小决定。实际上是:设置容器布局为null,然后通过setSize设置大小,setLocation设置位置。

部分情况不适用该布局方式的原因:坐标体系在不同的操作系统不能保证相同

[例1]设置一个按钮,从界面上方以抛物线的形式掉下来。

import javax.swing.*;
import java.awt.*;
class Layout1 extends JFrame implements Runnable{
	private JButton jbt = new JButton();
	int X,Y = 0;
	public Layout1(){
		jbt.setIcon(new ImageIcon("img.jpg"));
		this.setLayout(null);  
		jbt.setSize(80,60);		
		this.add(jbt);		
		this.setSize(300,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		Thread th = new Thread(this);
		th.start();
	}
	public void run(){
		while(true){
			try{Thread.sleep(20)  ;} catch(Exception e){}
			X++;  Y=X*X/100;
			jbt.setLocation(X,Y);
		}
	}
	public static void main(String[] args) throws Exception  {
		new Layout1();
	}
}

[例2]小球自由落体掉下,弹起来,再次自由落体。

import javax.swing.*;
import java.awt.*;
class Layout1 extends JFrame implements Runnable{
	private JButton jbt = new JButton();
	int Y = 0;
	int DIR = 1;
	public Layout1(){
		Icon icon = new ImageIcon("img.jpg");
		jbt.setIcon(icon);
		this.setLayout(null);  
		jbt.setSize(icon.getIconWidth(),icon.getIconHeight());		
		this.add(jbt);		
		this.setSize(300,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		Thread th = new Thread(this);
		th.start();
	}
	public void run(){
		while(true){
			try{Thread.sleep(20);} catch(Exception e){}
			if(DIR==1)  {	Y+=2; 	if(Y>=(this.getHeight()-jbt.getHeight())) DIR=0; }
			else if(DIR==0)  {	Y--; 	 if(Y<=0) DIR=1; }
			jbt.setLocation(60,Y);
		}
	}
	public static void main(String[] args) throws Exception {
		new Layout1();
	}
}

8.5 事件监听机制

Java事件处理在java.awt.event包中。

事件:程序对某些操作的响应。

点击按钮,打印“Hello”的三个步骤是:(其余都类似)

  • 事件响应类必须能够听到相应的命令(加一个耳朵)
    点击按钮如果要响应,必须让响应类实现java.awt.event.ActionListener接口
  • 将事件响应的代码,放在接口中重写的函数
  • 绑定,确保按钮发出命令,响应对象能够执行

①实现接口(监听器)、②编写函数、③绑定,叫做事件监听机制

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Printer implements ActionListener{ 			//①实现接口
	public void actionPerformed(ActionEvent e){     //②事件响应代码
		System.out.println("Hello");
	}
}

class EventTest extends JFrame{
	private JButton jbt = new JButton("按钮");
	public EventTest(){
		Printer p = new Printer();   
		jbt.addActionListener(p); 					//③绑定,确保按钮发出命令,p能够执行
		this.add(jbt,BorderLayout.NORTH);
		this.setSize(300,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args)    {
		new EventTest();
	}
}

更加简洁的方法是:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class EventTest extends JFrame implements ActionListener{
	private JButton jbt = new JButton("按钮");
	public EventTest(){
		jbt.addActionListener(this); 
		this.add(jbt,BorderLayout.NORTH);
		this.setSize(300,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public void actionPerformed(ActionEvent e){     
		System.out.println("Hello");
	}
	public static void main(String[] args){
		new EventTest();
	}
}

Java中定义,不同的事件,由不同的XXXListener来监听。

事件响应函数中,ActionEvent参数表示命令发出时,封装的命令发出方的信息

[例]实现两个按钮,一个按钮点击,界面变红,另一个点击,界面变蓝。

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

class EventTest extends JFrame implements ActionListener{
	private JButton jbt1 = new JButton("变红");	
	private JButton jbt2 = new JButton("变蓝");
	private JPanel jpl = new JPanel();
	public EventTest(){
		this.add(jbt1,BorderLayout.NORTH);
		this.add(jbt2,BorderLayout.SOUTH);		
		this.add(jpl,BorderLayout.CENTER);		
		jbt1.addActionListener(this); 
		jbt2.addActionListener(this); 
		this.setSize(300,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public void actionPerformed(ActionEvent e){     
		if(e.getSource()==jbt1){		//getSource()函数可以得知事件的源头
			jpl.setBackground(Color.red);
		}else{
			jpl.setBackground(Color.blue);			
		}
	}
	public static void main(String[] args)    {
		new EventTest();
	}
}

8.6 常见监听事件处理

下列Listener,分别监听以下事件:

  • ==ActionListener==:监听按钮点击,文本框内回车,菜单单击、其他支持单击响应的控件,以及一些拥有addActionListener函数的控件

  • ==ItemListener==:监听选项变化时要响应的事件,如下拉菜单等

    下拉菜单中有红绿蓝三个选项,选择时,界面自动变色

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class EventTest extends JFrame implements ItemListener {
	private JComboBox jcb = new JComboBox();	
	private JPanel jpl = new JPanel();
	public void itemStateChanged(ItemEvent e){
		if(jcb.getSelectedItem().equals("红")){
			jpl.setBackground(Color.red);
		}else if(jcb.getSelectedItem().equals("绿")){
			jpl.setBackground(Color.green);			
		}else{
			jpl.setBackground(Color.blue);			
		}
	}
	public EventTest(){
		this.add(jcb,BorderLayout.NORTH);	
		this.add(jpl,BorderLayout.CENTER);			
		jcb.addItemListener(this);  
		jcb.addItem("红");			jcb.addItem("绿");		jcb.addItem("蓝");		
		this.setSize(300,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args)    {
		new EventTest();
	}
}
  • ==MouseListener==:监听鼠标操作(单击,双击,进入,离开等)

[例1]鼠标进入按钮,按钮变红,退出,按钮变白。

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

class EventTest extends JFrame implements MouseListener{
	private JButton jbt = new JButton("按钮");
	public EventTest(){
		jbt.addMouseListener(this); 
		this.add(jbt,BorderLayout.NORTH);
		this.setSize(300,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public void mouseClicked(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {
		jbt.setBackground(Color.red);
	}
	public void mouseExited(MouseEvent e) {
		jbt.setBackground(Color.white);
	}
	public void mousePressed(MouseEvent e) {}
	public void mouseReleased(MouseEvent e) {}
	public static void main(String[] args)    {
		new EventTest();
	}
}

[例2]界面上有一个图片,鼠标进入,图片随机躲开。

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

class EventTest extends JFrame implements MouseListener{
	private JLabel jlb = new JLabel();
	int X=50,Y=50;
	public EventTest(){
		Icon icon = new ImageIcon("img.jpg");
		jlb.setIcon(icon);
		this.setLayout(null);		this.add(jlb);
		jlb.setSize(icon.getIconWidth(),icon.getIconHeight());
		jlb.setLocation(X,Y);
		jlb.addMouseListener(this); 
			
		this.setSize(400,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public void mouseClicked(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {
		X = (int)(Math.random()*this.getWidth())-jlb.getWidth();
		Y = (int)(Math.random()*this.getHeight())-jlb.getHeight();
		jlb.setLocation(X,Y);
	}
	public void mouseExited(MouseEvent e) {}
	public void mousePressed(MouseEvent e) {}
	public void mouseReleased(MouseEvent e) {}
	public static void main(String[] args)    {
		new EventTest();
	}
}
  • ==MouseMotionListener==:监听鼠标移动和拖动操作

[例]鼠标在界面上移动,标题栏显示鼠标的当前坐标。

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

class EventTest extends JFrame implements MouseMotionListener{
	public EventTest(){
		this.addMouseMotionListener(this);
		this.setSize(400,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public void mouseDragged(MouseEvent e) {
		String str = e.getX() + "," + e.getY();
		this.setTitle(str);
	}
	public void mouseMoved(MouseEvent e)  {

	}
	public static void main(String[] args)    {
		new EventTest();
	}
}

作业:界面上有一个图片,鼠标可以将图片从一个地方拖动到另一个地方。

作业:界面上有10个图片,鼠标可以将某个图片从一个地方拖动到另一个地方。

  • ==KeyListener==:监听键盘输入时要响应的事件(如俄罗斯方块)

[例]使用键盘上的上下左右键,能控制界面上一个图片的上下左右移动。

[注意]键盘事件一般被更大的容器先截获

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

class EventTest extends JFrame implements KeyListener{
	private JLabel jlb = new JLabel();
	int X=50,Y=50;
	public EventTest(){
		Icon icon = new ImageIcon("img.jpg");
		jlb.setIcon(icon);
		this.setLayout(null);		this.add(jlb);
		jlb.setSize(icon.getIconWidth(),icon.getIconHeight());
		jlb.setLocation(X,Y);
		this.addKeyListener(this);		//键盘事件应通过界面截获
		this.setSize(400,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}	
	public void keyPressed(KeyEvent e) {
		int keyCode = e.getKeyCode();
		if(keyCode==KeyEvent.VK_UP){
			Y-=30;
		}else if(keyCode==KeyEvent.VK_DOWN){
			Y+=30;
		}else if(keyCode==KeyEvent.VK_LEFT){
			X-=30;
		}else if(keyCode==KeyEvent.VK_RIGHT){
			X+=30;
		}
		jlb.setLocation(X,Y);
	} 
	public void keyReleased(KeyEvent e) {} 
	public void keyTyped(KeyEvent e)  {}	
	public static void main(String[] args)    {
		new EventTest();
	}
}

[综合例题]界面中一个图片掉下,鼠标进入,暂停,离开,继续掉。

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

class EventTest extends JFrame implements Runnable,MouseListener {
	private JLabel jlb = new JLabel();
	int X=50,Y=50;
	boolean RUN = true;
	public EventTest(){
		Icon icon = new ImageIcon("img.jpg");
		jlb.setIcon(icon);
		this.setLayout(null);		this.add(jlb);
		jlb.setSize(icon.getIconWidth(),icon.getIconHeight());
		jlb.setLocation(X,Y);
		this.addMouseListener(this);
		this.setSize(400,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		new Thread(this).start();
	}	
	public void mouseClicked(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {
		RUN = false;
	}
	public void mouseExited(MouseEvent e) {
		RUN = true;
		new Thread(this).start();
	}
	public void mousePressed(MouseEvent e) {}
	public void mouseReleased(MouseEvent e) {}	
	
	public void run(){
		while(RUN){
			try{ Thread.sleep(100); }catch(Exception e){}
			Y+=20;		
			jlb.setLocation(X,Y);
		}
	}
		
	public static void main(String[] args)    {
		new EventTest();
	}
}
  • AdjustmentListener:监听一些具有调整功能的控件(如滚动条)

  • FocusListener:监听当控件获得或者失去焦点时要响应的事件

    image-20220420230534660

​ 如焦点到一行时上面的提示信息消失,移除后提示信息继续存在。

8.7 Java绘图

8.7.1 绘制基本图形

用控件组成的界面,一般叫做高级界面;纯粹用绘画方式组成的界面,一般叫做低级界面。

低级界面开发的原理:以面板为画布,画布上画内容,将画布放在JFrame上,画布单独编写一个类

预备知识:

  • 画布上画内容,将该工作重写在从父类继承过来的paint函数中
  • paint函数会自动调用;如果手工调用,只能用repaint函数
  • 具体画图,采用paint函数中的Graphics参数(相当于画笔

注意:Graphics有一个子类Graphics2D,画笔可以直接转换成该类型,Graphics2D内拥有更加丰富的画笔功能

基础演示:

import javax.swing.*;
import java.awt.*;
class MyCanvas extends JPanel{
	public void paint(Graphics g){
		Graphics2D g2d = (Graphics2D)g;
		g2d.setStroke(new BasicStroke(50));		//设置粗细
		g2d.setColor(Color.red);
		g2d.drawLine(0,0,100,100);
	}
}

class Paint1 extends JFrame{
	private MyCanvas mc = new MyCanvas();
	public Paint1(){
		this.add(mc);
		this.setSize(800,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args)    {
		new Paint1();
	}
}

[例1]界面上,不断随机位置出现随机颜色的线段。

import javax.swing.*;
import java.awt.*;
class MyCanvas extends JPanel implements Runnable{
	public MyCanvas(){
		new Thread(this).start();
	}
	public void paint(Graphics g){
		Graphics2D g2d = (Graphics2D)g;
		g2d.setStroke(new BasicStroke((int)(Math.random()*50)));
		g2d.setColor(new Color((int)(Math.random()*256),(int)(Math.random()*256),(int)(Math.random()*256)));
		g2d.drawLine((int)(Math.random()*this.getWidth()),
		                     (int)(Math.random()*this.getHeight()),
		                     (int)(Math.random()*this.getWidth()),
		                     (int)(Math.random()*this.getHeight()));
	}
	public void run(){
		while(true){
			try{ Thread.sleep(50);	} catch(Exception e){}
			repaint();
		}
	}
}

class Paint1 extends JFrame{
	private MyCanvas mc = new MyCanvas();
	public Paint1(){
		this.add(mc);
		this.setSize(800,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args)    {
		new Paint1();
	}
}

[例2]界面上,鼠标拖动,可以连续画线。

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class MyCanvas extends JPanel implements MouseMotionListener{
	int X=0,Y=0;
	public MyCanvas(){
		this.addMouseMotionListener(this);
	}
	public void mouseDragged(MouseEvent e) {
		X = e.getX();  Y = e.getY();
		this.repaint();
	}
	public void mouseMoved(MouseEvent e)  {}		
	
	public void paint(Graphics g){
		Graphics2D g2d = (Graphics2D)g;
		g2d.setColor(Color.green);
		g2d.fillOval(X,Y,20,20); //参数(int x,int y,int width,int height)函数用当前颜色填充由指定矩形界定的椭圆
	}
}

class Paint1 extends JFrame{
	private MyCanvas mc = new MyCanvas();
	public Paint1(){
		this.add(mc);
		this.setSize(800,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args)    {
		new Paint1();
	}
}

8.7.2 绘制图像

绘制图像,使用的是画笔的drawImage函数。

  • 原图绘制:drawImage(Image img,int x,int y, ImageObserver observer)
  • 原图缩放绘制:drawImage(Image img,int x,int y,int width,int height, ImageObserver observer)

基础演示:

import javax.swing.*;
import java.awt.*;
class MyCanvas extends JPanel{
	private	Image img = Toolkit.getDefaultToolkit().createImage("img.jpg");
	public void paint(Graphics g){
		Graphics2D g2d = (Graphics2D)g;
		g2d.drawImage(img,50,60,this);
		g2d.drawImage(img,200,300,200,50,this);
	}
}

class Paint1 extends JFrame{
	private MyCanvas mc = new MyCanvas();
	public Paint1(){
		this.add(mc);
		this.setSize(800,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args)    {
		new Paint1();
	}
}

[例]一个图像,在界面上渐渐放大。

import javax.swing.*;
import java.awt.*;
class MyCanvas extends JPanel implements Runnable{
	private	Image img = Toolkit.getDefaultToolkit().createImage("img.jpg");
	int X=0,Y=0;
	public MyCanvas(){   new Thread(this).start(); }
	public void run(){
		while(true){
			try{  Thread.sleep(50); } catch(Exception ex){}
			X+=10; Y+=10;
			repaint();
		}
	}
	public void paint(Graphics g){
		Graphics2D g2d = (Graphics2D)g;
		g2d.drawImage(img,20,20,X,Y,this);
	}
}

class Paint1 extends JFrame{
	private MyCanvas mc = new MyCanvas();
	public Paint1(){
		this.add(mc);
		this.setSize(800,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args)    {
		new Paint1();
	}
}
  • 截取图像部分绘制:drawImage(Image img,
                                                       int dx1,int dy1,int dx2,int dy2,       //d指**destination**
                                                                         int sx1,int sy1,int sx2,int sy2,         //s指**source**
                                                                         ImageObserver observer) 
    

[例]将原图的左边一半,右边一半,上面一半,下面一半分别绘制。

import javax.swing.*;
import java.awt.*;
class MyCanvas extends JPanel{
	private	Image img = Toolkit.getDefaultToolkit().createImage("img.jpg");
	public void paint(Graphics g){
		Graphics2D g2d = (Graphics2D)g;
		g2d.drawImage(img,0,0,100,100,0,0,img.getWidth(this)/2,img.getHeight(this), this);
        g2d.drawImage(img,200,200,300,300,img.getWidth(this)/2,0,img.getWidth(this),img.getHeight(this), 
                                		this);                
        g2d.drawImage(img,300,50,400,100,0,0,img.getWidth(this),img.getHeight(this)/2, this); 
		g2d.drawImage(img,400,200,500,300,0,
              							img.getHeight(this)/2,img.getWidth(this),img.getHeight(this),
                                		this);                	
	}
}

class Paint1 extends JFrame{
	private MyCanvas mc = new MyCanvas();
	public Paint1(){
		this.add(mc);
		this.setSize(800,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args)    {
		new Paint1();
	}
}

8.7.3 图像旋转

利用rotate函数。 rotate(double theta,double x,double y)

  • theta - 以弧度表示的旋转角度(顺时针)
  • x - 旋转原点的x坐标
  • y - 旋转原点的y坐标
import javax.swing.*;
import java.awt.*;
class MyCanvas extends JPanel{
	private	Image img = Toolkit.getDefaultToolkit().createImage("img.jpg");
	public void paint(Graphics g){
		Graphics2D g2d = (Graphics2D)g;
		g2d.drawImage(img,200,300,this);
		g2d.rotate(Math.PI/2,200+img.getWidth(this)/2,300+img.getHeight(this)/2);
		g2d.drawImage(img,200,300,this);
	}
}

class Paint1 extends JFrame{
	private MyCanvas mc = new MyCanvas();
	public Paint1(){
		this.add(mc);
		this.setSize(800,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args)    {
		new Paint1();
	}
}

[例]图像边移动边旋转。

import javax.swing.*;
import java.awt.*;
class MyCanvas extends JPanel implements Runnable{
	private	Image img = Toolkit.getDefaultToolkit().createImage("img.jpg");
	double r = 0;
	int X=0;
	public MyCanvas(){  new Thread(this).start(); }
	public void run(){
		while(true){
			try{ Thread.sleep(50);	} catch(Exception e){}
			r = r + 0.1;
			X+=10;
			repaint();
		}
	}
	public void paint(Graphics g){
		Graphics2D g2d = (Graphics2D)g;
		g2d.setColor(Color.white);
		g2d.fillRect(0,0,this.getWidth(),this.getHeight());		//实现之前经过的图像不在界面上显示
		g2d.rotate(r,0+X+img.getWidth(this)/2,0+X+img.getHeight(this)/2);
		g2d.drawImage(img,0+X,0+X,this);
	}
}

class Paint1 extends JFrame{
	private MyCanvas mc = new MyCanvas();
	public Paint1(){
		this.add(mc);
		this.setSize(800,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
	}
	public static void main(String[] args)    {
		new Paint1();
	}
}

ch9.Java网络编程

9.1 网络基础知识

网络中有两个重要的协议,分别是:

  • TCP:传输控制协议 面向连接 打电话

  • UDP:数据包协议 面向数据包 发短信

使用较多的是TCP。

网络应用程序有微信、QQ、抖音、浏览器等,Java网络编程讲解的是这些网络应用程序最底层的内容。

有两种服务器和客户端的通信模式:

  • C/S模式:客户端/服务器 客户安装应用程序,和服务器通信 微信、QQ

  • B/S模式:浏览器/服务器 客户端安装浏览器,和服务器通信 www.csu.edu.cn在浏览器中运行

信息通过客户端和服务器通信,客户端之间的通信,靠服务器转发;故网络应用程序,一定要分为客户端和服务器程序。

网络上的计算机靠IP地址来定位,出现在客户端

用端口号(port)确定不同的网络应用程序类型,由服务器决定

端口号用数字表示,一般0-65535之间,有些非常常见的应用程序,已经预设了端口号:HTTP(浏览器:80),FTP(21)等等。一般应用程序,建议使用4位数端口号,以免冲突

9.2 单个客户端连接服务器

场景:客户端给服务器发送一句:你好,服务器回应,欢迎。

客户端连接服务器的步骤如下:==[使用java.net包]==

  • 1:服务器先运行,确定端口号
  • 2:客户端通过服务器IP地址+端口号,连上
  • 3:通信

运用到的类如下:

  • ServerSocket:启动服务器;接受客户端连接
  • Socket: 客户端连上服务器,双方通信

Server1.java:

import java.net.*;
import java.io.*;
class Server1 {
	public static void main(String[] args) throws Exception   {
		ServerSocket ss = new ServerSocket(9999);
		Socket s = ss.accept(); 
		InputStreamReader isr = new InputStreamReader(s.getInputStream());
		BufferedReader br = new BufferedReader(isr);
		String msg = br.readLine();
		System.out.println(msg);
		PrintStream ps = new PrintStream(s.getOutputStream());
		ps.println("欢迎你连上服务器!");
	}
}

Client1.java:

import java.net.*;
import java.io.*;
class Client1 {
	public static void main(String[] args) throws Exception   {
		Socket s = new Socket("172.17.10.147", 9999);
		PrintStream ps = new PrintStream(s.getOutputStream());
		ps.println("你好,我是客户端");
		BufferedReader br = 
			new BufferedReader(new InputStreamReader(s.getInputStream()));
		String str = br.readLine();
		System.out.println(str);
	}
}

[例1]客户端和服务器互相聊天:

客户端和服务器都有文本框,输入内容,回车(ActionListener),信息发给对方显示;反过来也支持。

Server2.java:

import java.net.*;
import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Server extends JFrame implements ActionListener,Runnable{
	private JTextField jtf = new JTextField();
	private JTextArea jta = new JTextArea();
	private BufferedReader br = null;
	private PrintStream ps = null;
	public Server() throws Exception{
		this.setTitle("服务器");
		this.add(jtf,BorderLayout.SOUTH);
		this.add(jta,BorderLayout.CENTER);
		jtf.addActionListener(this);
		this.setSize(300,500);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		ServerSocket ss = new ServerSocket(9999);
		Socket s = ss.accept(); 
		br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		ps = new PrintStream(s.getOutputStream());
		new Thread(this).start();
	}
	public void run() {
	  while(true){
		try{	
			String msg = br.readLine();
			jta.append(msg + "\n");
		}catch(Exception e){}
	  }
	}
	public void actionPerformed(ActionEvent e){
		jta.append(jtf.getText() + "\n");
		ps.println("服务器说:" + jtf.getText());
	}
	public static void main(String[] args) throws Exception   {
		new Server();	
	}
}

Client2.java:

import java.net.*;
import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Client extends JFrame implements ActionListener,Runnable{
	private JTextField jtf = new JTextField();
	private JTextArea jta = new JTextArea();
	private BufferedReader br = null;
	private PrintStream ps = null;
	public Client() throws Exception{
		this.setTitle("客户端");
		this.add(jtf,BorderLayout.NORTH);
		this.add(jta,BorderLayout.CENTER);
		jtf.addActionListener(this);
		this.setSize(300,500);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		Socket s = new Socket("172.17.10.147",9999); 
		br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		ps = new PrintStream(s.getOutputStream());
		new Thread(this).start();
	}
	public void run() {
 	  while(true){
	  	try{	
			String msg = br.readLine();
			jta.append(msg + "\n");
		}catch(Exception e){}
 	  }
	}
	public void actionPerformed(ActionEvent e){
		jta.append(jtf.getText() + "\n");
		ps.println("客户端说:" + jtf.getText());
	}
	public static void main(String[] args) throws Exception   {
		new Client();
	}
}

[例2·B/S典型例子]客户端给服务器发信息,如果发N字母,服务器发给客户端一个随机数字;如果发M字母,服务器给客户端发送一个欢迎文本。

Server3.java:

import java.net.*;
import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Server extends JFrame implements Runnable{
	private BufferedReader br = null;
	private PrintStream ps = null;
	public Server() throws Exception{
		this.setTitle("服务器");
		this.setSize(200,200);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		ServerSocket ss = new ServerSocket(9999);
		Socket s = ss.accept(); 
		br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		ps = new PrintStream(s.getOutputStream());
		new Thread(this).start();
	}
	public void run() {
	  while(true){
		try{	
			String msg = br.readLine();
			if(msg.equals("N")){  ps.println("数字:" + Math.random()); }
			if(msg.equals("M")){  ps.println("文本:欢迎"); }
		}catch(Exception e){}
	  }
	}
	public static void main(String[] args) throws Exception   {
		new Server();	
	}
}

Client3.java:

import java.net.*;
import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Client extends JFrame implements ActionListener,Runnable{
	private JTextField jtf = new JTextField();
	private JTextArea jta = new JTextArea();
	private BufferedReader br = null;
	private PrintStream ps = null;
	public Client() throws Exception{
		this.setTitle("客户端");
		this.add(jtf,BorderLayout.NORTH);
		this.add(jta,BorderLayout.CENTER);
		jtf.addActionListener(this);
		this.setSize(300,500);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		Socket s = new Socket("172.17.10.147",9999); 
		br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		ps = new PrintStream(s.getOutputStream());
		new Thread(this).start();
	}
	public void run() {
 	  while(true){
	  	try{	
			String msg = br.readLine();
			jta.append(msg + "\n");
		}catch(Exception e){}
 	  }
	}
	public void actionPerformed(ActionEvent e){
		ps.println(jtf.getText());
	}
	public static void main(String[] args) throws Exception{
		new Client();
	}
}

9.3 多个客户端连接服务器

应用场合:多个客户端连上服务器,客户端之间的通信依靠服务器转发。

9.3.1 服务器端群聊案例

[例1]多个客户端连上服务器:服务器端有一个界面,界面上有一个多行文本框,多个客户端连接,连上就在服务器显示“有人连上” 。

Server1.java:

import java.net.*;
import java.io.*;
import javax.swing.*;
class Server extends JFrame implements Runnable{
	private JTextArea jta = new JTextArea();
	private ServerSocket ss = null;
	public Server() throws Exception{
		this.add(jta);
		this.setSize(200,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		ss = new ServerSocket(9999);		
		new Thread(this).start();
	}
	public void run(){
		while(true){
			try{
				Socket s = ss.accept();
				jta.append("有人连上!!\n");
		    }catch(Exception ex){}
		}
	}
	public static void main(String[] args) throws Exception   {
		new Server();
	}
}

Client1.java:

import java.net.*;
import java.io.*;
class Client {
	public static void main(String[] args) throws Exception   {
		Socket s = new Socket("172.17.10.148", 9999);		
	}
}

[例2]服务器端群聊:每个客户端都可以输入信息,回车发送,发送之后的信息,在服务器端显示。

类中类,多线程,每个客户端连接对应一个线程。

Server2.java:

import java.net.*;
import java.io.*;
import java.awt.*;
import javax.swing.*;
class Server  extends JFrame implements Runnable{
	private JTextArea jta = new JTextArea();
	private ServerSocket ss = null;
	public Server() throws Exception{
		this.add(jta);		
		jta.setFont(new Font("宋体",Font.BOLD,30));
		this.setSize(200,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		ss = new ServerSocket(9999);		
		new Thread(this).start();
	}
	public void run(){
		while(true){
			try{
				Socket s = ss.accept();
				ChatThread ct = new ChatThread(s);		ct.start();
		    }catch(Exception ex){}
		}
	}
	class ChatThread extends Thread{
		BufferedReader br = null;
		ChatThread(Socket s) throws Exception{
			br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		}
		public void run(){
			while(true){
				try{
					String msg = br.readLine();
					jta.append(msg + "\n");
				}catch(Exception e){}
			}
		}
	}
	public static void main(String[] args) throws Exception   {
		new Server();
	}
}

Client2.java:

import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Client  extends JFrame implements ActionListener {
	private JTextField jtf = new JTextField();
	private PrintStream ps = null;
	private String nickName = null;
	public Client() throws Exception{
		this.add(jtf,BorderLayout.SOUTH);
		jtf.addActionListener(this);
		jtf.setFont(new Font("宋体",Font.BOLD,30));
		this.setSize(300,200);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		nickName = JOptionPane.showInputDialog("请您输入昵称");
		Socket s = new Socket("172.17.10.148", 9999);
		ps = new PrintStream(s.getOutputStream());
	}
	public void actionPerformed(ActionEvent e){
		ps.println(nickName + "说:" + jtf.getText());
	}
	public static void main(String[] args) throws Exception   {
		new Client();
	}
}

9.3.2 客户端群聊案例

[例3]客户端群聊:每个客户端都可以输入信息,回车发送,发送之后的信息,在每个客户端显示[服务器转发]。

Server3.java:

import java.net.*;
import java.io.*;
import java.awt.*;
import java.util.*;
import javax.swing.*;
class Server  extends JFrame implements Runnable{
	private ServerSocket ss = null;
	private ArrayList<ChatThread> users = new ArrayList<ChatThread>();	//一维变长数组,存放ChatThread类型变量
	public Server() throws Exception{
		this.setSize(200,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		ss = new ServerSocket(9999);		
		new Thread(this).start();
	}
	public void run(){
		while(true){
			try{
				Socket s = ss.accept();
				ChatThread ct = new ChatThread(s);		
				users.add(ct);				ct.start();
		    }catch(Exception ex){}
		}
	}
	class ChatThread extends Thread{
		BufferedReader br = null;
		PrintStream ps = null;
		ChatThread(Socket s) throws Exception{
			br = new BufferedReader(new InputStreamReader(s.getInputStream()));
			ps = new PrintStream(s.getOutputStream());
		}
		public void run(){
			while(true){
				try{
					String msg = br.readLine();
					for(ChatThread ct : users){
						ct.ps.println(msg);
					}
				}catch(Exception e){}
			}
		}
	}	
	public static void main(String[] args) throws Exception   {
		new Server();
	}
}

Client3.java:

import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Client  extends JFrame implements ActionListener,Runnable {
	private JTextField jtf = new JTextField();
	private JTextArea jta = new JTextArea();
	private PrintStream ps = null;
	private BufferedReader br = null;
	private String nickName = null;
	public Client() throws Exception{
		this.add(jta,BorderLayout.CENTER);
		this.add(jtf,BorderLayout.SOUTH);
		jtf.addActionListener(this);
		jtf.setFont(new Font("宋体",Font.BOLD,30));
		jta.setFont(new Font("宋体",Font.BOLD,30));
		this.setSize(400,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		nickName = JOptionPane.showInputDialog("请您输入昵称");
		Socket s = new Socket("172.17.10.148", 9999);
		ps = new PrintStream(s.getOutputStream());
		br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		new Thread(this).start();
	}
	public void run(){
		while(true){
			try{
				String msg = br.readLine();
				jta.append(msg + "\n");
			}catch(Exception e){}
		}		
	}
	public void actionPerformed(ActionEvent e){
		ps.println(nickName + "说:" + jtf.getText());
	}
	public static void main(String[] args) throws Exception   {
		new Client();
	}
}

[例4]客户端群聊:每个客户端增加一个按钮,点击按钮,该客户端下线。其他客户端显示该客户端下线的消息。

Server4.java:

import java.net.*;
import java.io.*;
import java.awt.*;
import java.util.*;
import javax.swing.*;
class Server  extends JFrame implements Runnable{
	private ServerSocket ss = null;
	private ArrayList<ChatThread> users = new ArrayList<ChatThread>();
	public Server() throws Exception{
		this.setSize(200,400);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		ss = new ServerSocket(9999);		
		new Thread(this).start();
	}
	public void run(){
		while(true){
			try{
				Socket s = ss.accept();
				ChatThread ct = new ChatThread(s);		
				users.add(ct);				ct.start();
		    }catch(Exception ex){}
		}
	}
	class ChatThread extends Thread{
		BufferedReader br = null;
		PrintStream ps = null;
		String nickName = null;
		ChatThread(Socket s) throws Exception{
			br = new BufferedReader(new InputStreamReader(s.getInputStream()));
			ps = new PrintStream(s.getOutputStream());
		}
		public void run(){
			while(true){
				try{
					String msg = br.readLine();
					String[] strs = msg.split(":"); 
					if(strs[0].equals("NICK")){
					   nickName = strs[1];
					}else if(strs[0].equals("XIAXIAN")){		//传递
						for(ChatThread ct : users){
							if(ct.nickName.equals(strs[1])){
								users.remove(ct);
							}else{
								ct.ps.println(strs[1]+"下线啦!");
							}							
						}
					}else{
						for(ChatThread ct : users){
							ct.ps.println(msg);
						}						
					}
				}catch(Exception e){}
			}
		}
	}	
	public static void main(String[] args) throws Exception   {
		new Server();
	}
}

Client4.java:

import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Client  extends JFrame implements ActionListener,Runnable {
	private JButton jbt = new JButton("下线");
	private JTextField jtf = new JTextField();
	private JTextArea jta = new JTextArea();
	private PrintStream ps = null;
	private BufferedReader br = null;
	private String nickName = null;
	public Client() throws Exception{
		this.add(jbt,BorderLayout.NORTH);
		this.add(jta,BorderLayout.CENTER);
		this.add(jtf,BorderLayout.SOUTH);
		jtf.addActionListener(this);
		jbt.addActionListener(this);
		jtf.setFont(new Font("宋体",Font.BOLD,30));
		jta.setFont(new Font("宋体",Font.BOLD,30));
		this.setSize(400,600);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setVisible(true);
		nickName = JOptionPane.showInputDialog("请您输入昵称");
		Socket s = new Socket("172.17.10.148", 9999);
		ps = new PrintStream(s.getOutputStream());
		br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		ps.println("NICK:"+nickName);
		new Thread(this).start();
	}
	public void run(){
		while(true){
			try{
				String msg = br.readLine();
				jta.append(msg + "\n");
			}catch(Exception e){}
		}		
	}
	public void actionPerformed(ActionEvent e){
		if(e.getSource()==jtf){
			ps.println(nickName + "说:" + jtf.getText());
		}else{
			ps.println("XIAXIAN:" + nickName);
			System.exit(0);
		}
	}
	public static void main(String[] args) throws Exception   {
		new Client();
	}
}

另例:客户端群聊:每个客户端增加一个按钮,点击按钮,该客户端下线。其他客户端显示该客户端下线的消息——服务器要对信息进行分类处理。

9.3.3 客户端私聊案例

客户端还要显示在线客户名单,每个客户端可以选择某人,输入信息,回车发送,发送之后的信息,在指定客户端显示。

  • 每个用户登录,用户昵称发给服务器,服务器转发给客户端显示用户列表

  • 客户端选择人员之后,私聊对象的昵称发给服务器端,服务器端不再将聊天信息转发给所有客户端,而发给特定的客户端(加一个判断)

Server1.java:

import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.HashMap;
class Server  extends JFrame implements Runnable,ActionListener{
	private List users = new List();
	private JButton jbt = new JButton("远程关闭");
	private ServerSocket ss = null;
	private HashMap<String,ChatThread> cts = new HashMap<String,ChatThread>();
	public Server() throws Exception{
		this.add(users,BorderLayout.CENTER);
		this.add(jbt,BorderLayout.SOUTH);
		jbt.addActionListener(this);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setSize(200,300);
		this.setVisible(true);
		ss = new ServerSocket(9999);
		new Thread(this).start();
	}
	public void actionPerformed(ActionEvent e){
		String selectedUser = users.getSelectedItem();
		String msg = "LOGOUT:"+selectedUser;
		ChatThread ct = cts.get(selectedUser);
		ct.ps.println(msg);
		users.remove(selectedUser);
		cts.remove(selectedUser);
	}
	public void run(){
		while(true){
			try{
				Socket s = ss.accept();
				ChatThread ct = new ChatThread(s);
			}catch(Exception e){}		
		}
	}
	class ChatThread extends Thread{
		PrintStream ps = null;
		BufferedReader br = null;
		String nickName = null;
		ChatThread(Socket s) throws Exception{
			ps = new PrintStream(s.getOutputStream());
			br = new BufferedReader(new InputStreamReader(s.getInputStream()));
			this.start();
		}
		public void run(){
			while(true){
				try{
					String msg = br.readLine();
					String[] msgs = msg.split(":");
					if(msgs[0].equals("LOGIN")){
						nickName = msgs[1];
						users.add(nickName);
						cts.put(nickName,this);
					}
				}catch(Exception ex){}
			}
		}
	}
	public static void main(String[] args) throws Exception   {
		new Server();
	}
}

Client1.java:

import java.net.*;
import java.io.*;
import javax.swing.*;
class Client extends JFrame implements Runnable{
	private String nickName = null;
	private PrintStream ps = null;
	private BufferedReader br = null;	
	public Client() throws Exception{
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setSize(300,200);
		this.setVisible(true);
		nickName = JOptionPane.showInputDialog("输入昵称");
		Socket s = new Socket("172.17.10.147", 9999);
		ps = new PrintStream(s.getOutputStream());
		br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		ps.println("LOGIN:"+nickName);
		new Thread(this).start();				
	}
	public void run(){
		while(true){
			try{
				String msg = br.readLine();
				String[] msgs = msg.split(":");
				if(msgs[0].equals("LOGOUT")){
					JOptionPane.showMessageDialog(this,"您被踢出!");
					this.dispose();
				}
			}catch(Exception e){}		
		}
	}
	public static void main(String[] args) throws Exception   {
			new Client();
	}
}

9.4 用户管理和控制

服务器可以监控各个客户端。

两个客户端输入昵称,连接到服务器,服务器显示昵称列表。选择某人,将其远程关闭

  • 定义客户端连接的信息格式为:LOGIN:昵称
  • 定义让某个客户端下线信息格式为:LOGOUT:昵称

Server1.java:

import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.HashMap;
class Server  extends JFrame implements Runnable,ActionListener{
	private List users = new List();
	private JButton jbt = new JButton("远程关闭");
	private ServerSocket ss = null;
	private HashMap<String,ChatThread> cts = new HashMap<String,ChatThread>();
	public Server() throws Exception{
		this.add(users,BorderLayout.CENTER);
		this.add(jbt,BorderLayout.SOUTH);
		jbt.addActionListener(this);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setSize(200,300);
		this.setVisible(true);
		ss = new ServerSocket(9999);
		new Thread(this).start();
	}
	public void actionPerformed(ActionEvent e){
		String selectedUser = users.getSelectedItem();
		String msg = "LOGOUT:"+selectedUser;
		ChatThread ct = cts.get(selectedUser);
		ct.ps.println(msg);
		users.remove(selectedUser);
		cts.remove(selectedUser);
	}
	public void run(){
		while(true){
			try{
				Socket s = ss.accept();
				ChatThread ct = new ChatThread(s);
			}catch(Exception e){}		
		}
	}
	class ChatThread extends Thread{
		PrintStream ps = null;
		BufferedReader br = null;
		String nickName = null;
		ChatThread(Socket s) throws Exception{
			ps = new PrintStream(s.getOutputStream());
			br = new BufferedReader(new InputStreamReader(s.getInputStream()));
			this.start();
		}
		public void run(){
			while(true){
				try{
					String msg = br.readLine();
					String[] msgs = msg.split(":");
					if(msgs[0].equals("LOGIN")){
						nickName = msgs[1];
						users.add(nickName);
						cts.put(nickName,this);
					}
				}catch(Exception ex){}
			}
		}
	}
	public static void main(String[] args) throws Exception   {
		new Server();
	}
}

Client1.java:

import java.net.*;
import java.io.*;
import javax.swing.*;
class Client extends JFrame implements Runnable{
	private String nickName = null;
	private PrintStream ps = null;
	private BufferedReader br = null;	
	public Client() throws Exception{
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setSize(300,200);
		this.setVisible(true);
		nickName = JOptionPane.showInputDialog("输入昵称");
		Socket s = new Socket("172.17.10.147", 9999);
		ps = new PrintStream(s.getOutputStream());
		br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		ps.println("LOGIN:"+nickName);
		new Thread(this).start();				
	}
	public void run(){
		while(true){
			try{
				String msg = br.readLine();
				String[] msgs = msg.split(":");
				if(msgs[0].equals("LOGOUT")){
					JOptionPane.showMessageDialog(this,"您被踢出!");
					this.dispose();
				}
			}catch(Exception e){}		
		}
	}
	public static void main(String[] args) throws Exception   {
			new Client();
	}
}

[例1]两个客户端,每个客户端有一个图像掉下来,服务器端可以控制相应客户端暂停或继续。

定义客户端连接的信息格式为:LOGIN:昵称

  • 定义暂停为:ZT:昵称
  • 定义继续为:JX:昵称

Server2.java:

import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.HashMap;
class Server  extends JFrame implements Runnable,ActionListener{
	private List users = new List();
	private JButton jbt = new JButton("暂停");
	private JButton jbt2 = new JButton("继续");
	private ServerSocket ss = null;
	private HashMap<String,ChatThread> cts = new HashMap<String,ChatThread>();
	public Server() throws Exception{
		this.add(users,BorderLayout.CENTER);
		this.add(jbt,BorderLayout.SOUTH);
		this.add(jbt2,BorderLayout.NORTH);
		jbt.addActionListener(this);
		jbt2.addActionListener(this);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setSize(200,300);
		this.setVisible(true);
		ss = new ServerSocket(9999);
		new Thread(this).start();
	}
	public void actionPerformed(ActionEvent e){
		String selectedUser = users.getSelectedItem();
		String msg = null;
		if(e.getSource()==jbt){
			 msg = "ZT:"+selectedUser;			
		}else{			
			 msg = "JX:"+selectedUser;
		}
		ChatThread ct = cts.get(selectedUser);
		ct.ps.println(msg);		
	}
	public void run(){
		while(true){
			try{
				Socket s = ss.accept();
				ChatThread ct = new ChatThread(s);
			}catch(Exception e){}		
		}
	}
	class ChatThread extends Thread{
		PrintStream ps = null;
		BufferedReader br = null;
		String nickName = null;
		ChatThread(Socket s) throws Exception{
			ps = new PrintStream(s.getOutputStream());
			br = new BufferedReader(new InputStreamReader(s.getInputStream()));
			this.start();
		}
		public void run(){
			while(true){
				try{
					String msg = br.readLine();
					String[] msgs = msg.split(":");
					if(msgs[0].equals("LOGIN")){
						nickName = msgs[1];
						users.add(nickName);
						cts.put(nickName,this);
					}
				}catch(Exception ex){}
			}
		}
	}
	public static void main(String[] args) throws Exception   {
		new Server();
	}
}

Client2.java:

import java.net.*;
import java.io.*;
import javax.swing.*;
class Client extends JFrame implements Runnable{
	private String nickName = null;
	private PrintStream ps = null;
	private BufferedReader br = null;	
	private JLabel jlb = new JLabel(); 
	private int X=100,Y = 0;
	private ImageThread it = null;
	public Client() throws Exception{
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setLayout(null); 		this.add(jlb);
		Icon icon = new ImageIcon("img.jpg");
		jlb.setIcon(icon);
		jlb.setLocation(X,Y);		jlb.setSize(icon.getIconWidth(),icon.getIconHeight());
		this.setSize(300,500);
		this.setVisible(true);
		nickName = JOptionPane.showInputDialog("输入昵称");
		Socket s = new Socket("172.17.10.147", 9999);
		ps = new PrintStream(s.getOutputStream());
		br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		ps.println("LOGIN:"+nickName);
		new Thread(this).start();		
		it = new ImageThread();   it.start();		
	}
	public void run(){
		while(true){
			try{
				String msg = br.readLine();
				String[] msgs = msg.split(":");
				if(msgs[0].equals("ZT")){
					it.RUN = false;
				}
				if(msgs[0].equals("JX")){
					it = new ImageThread();   it.start();
				}
			}catch(Exception e){}		
		}
	}
	class ImageThread extends Thread{
		boolean  RUN = true;
		public void run(){
			while(RUN){
			  try{ Thread.sleep(100);	Y++; 
				      jlb.setLocation(X,Y);
			  }catch(Exception ex){}
			}
		
		}
	}
	public static void main(String[] args) throws Exception   {
			new Client();
	}
}

[例2]两个客户端,服务器产生一个随机数,随机数从每个客户端掉下来,某一个先掉到底部,告知服务器,服务器重新产生随机数,两者同时从最上面掉下来。

定义客户端连接的信息格式为:LOGIN:昵称

  • 定义发送随机数为:SJS:数字
  • 定义掉到底部为:DB:昵称

Server3.java:

import java.net.*;
import java.io.*;
import java.awt.*;
import javax.swing.*;
import java.util.HashMap;
import java.util.Set;
class Server  extends JFrame implements Runnable{
	private List users = new List();
	private ServerSocket ss = null;
	private HashMap<String,ChatThread> cts = new HashMap<String,ChatThread>();
	public Server() throws Exception{
		this.add(users,BorderLayout.CENTER);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setSize(200,300);
		this.setVisible(true);
		ss = new ServerSocket(9999);
		new Thread(this).start();
	}
	public void run(){
		while(true){
			try{
				Socket s = ss.accept();
				ChatThread ct = new ChatThread(s);
			}catch(Exception e){}		
		}
	}
	class ChatThread extends Thread{
		PrintStream ps = null;
		BufferedReader br = null;
		String nickName = null;
		ChatThread(Socket s) throws Exception{
			ps = new PrintStream(s.getOutputStream());
			br = new BufferedReader(new InputStreamReader(s.getInputStream()));
			this.start();
		}
		public void run(){
			while(true){
				try{
					String msg = br.readLine();
					String[] msgs = msg.split(":");
					if(msgs[0].equals("LOGIN")){
						nickName = msgs[1];
						users.add(nickName);
						cts.put(nickName,this);
					}else if(msgs[0].equals("DB")){
						String randStr = "SJS:"+Math.random();
						Set<String> nickNames = cts.keySet();
						for(String nk : nickNames){
							ChatThread ct = cts.get(nk);
							ct.ps.println(randStr);
						}
					}
				}catch(Exception ex){}
			}
		}
	}
	public static void main(String[] args) throws Exception   {
		new Server();
	}
}

Client3.java:

import java.net.*;
import java.io.*;
import javax.swing.*;
class Client extends JFrame implements Runnable{
	private String nickName = null;
	private PrintStream ps = null;
	private BufferedReader br = null;	
	private JLabel jlb = new JLabel(); 
	private int X=100,Y = 0;
	private ImageThread it = null;
	public Client() throws Exception{
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		this.setLayout(null); 		this.add(jlb);
		jlb.setText("欢迎参加本游戏");  jlb.setSize(200,50);
		jlb.setLocation(X,Y);	
		this.setSize(300,500);
		this.setVisible(true);
		nickName = JOptionPane.showInputDialog("输入昵称");
		Socket s = new Socket("172.17.10.147", 9999);
		ps = new PrintStream(s.getOutputStream());
		br = new BufferedReader(new InputStreamReader(s.getInputStream()));
		ps.println("LOGIN:"+nickName);
		new Thread(this).start();		
		it = new ImageThread();   it.start();		
	}
	public void run(){
		while(true){
			try{
				String msg = br.readLine();
				String[] msgs = msg.split(":");
				if(msgs[0].equals("SJS")){
					jlb.setText(msgs[1]);
					Y=0;					
				    jlb.setLocation(X,Y);
				}

			}catch(Exception e){}		
		}
	}
	class ImageThread extends Thread{
		boolean  RUN = true;
		public void run(){
			while(RUN){
			  try{ Thread.sleep(100);	Y++; 
				      jlb.setLocation(X,Y);
				      if(Y>=500){
				      	 ps.println("DB:" + nickName);
				      }
			  }catch(Exception ex){}
			}		
		}
	}
	public static void main(String[] args) throws Exception   {
			new Client();
	}
}

附:JavaSE思维导图

期末考试题型:选择题、填空题、判断题、改错题、问答题、程序题。

模块 内容 要点
Java语言基本语法 Java特点 1、跨平台;2、运行的基本原理(代码用java文件,可执行文件是.class);3、语法和C基本相同,没有指针;程序入口是main函数
Java语法 1、变量、数据类型:float和long定义比较特殊;各种数据类型的精度顺序,高精度不能直接赋值给低精度;不能用0和非0表示真假;变量的定义必须掌握;字符数据类型有转义字符,用 \?表示 2、运算:算术运算:+ - / % += -= = /= ++ — ; 关系运算:> < >= <= == !=;逻辑运算:&&
面向对象基本概念 类和对象 类的定义(要会编程);由类来实例化对象原理;对象名的引用性质(理解对象名重新指向另一个对象的情况)
成员变量和成员函数 成员变量和成员函数的定义;通过对象访问成员变量成员函数(.号);成员函数参数传递时的值传递和引用传递的区别(需要能够分析实例)
特殊的成员变量和成员函数 1、构造函数:定义方式、调用特点、垃圾收集机制 2、函数重载:三个条件满足其一即可 3、静态变量:定义方式、性质特点、如何调用(类名.或者对象名.来调用) 4、静态函数:定义方式(和静态变量类似)、性质特点(静态函数只能访问静态成员和函数中的局部变量)、如何调用(类名.或者对象名.来调用) 常见概念的英文形式必须要掌握 需要能够进行基本的编程
面向对象基本特征 封装 1、包:定义包(注意包的嵌套关系);导入包中的类 2、类:public类(定义方式、特点);普通类(定义方式、特点) 3、成员:四个访问控制符:public、protected、默认、private 理解它们的具体权限 需要能够进行基本的编程
继承 1、如何实现继承(extends)?Java继承的特点(只能使用继承的非private成员、Java不支持多重继承) 2、覆盖:如何编程才算覆盖(子类定义成员和父类名称、返回类类型、参数皆相同)?覆盖有什么特点(子类对象调用时,直接调用子类成员)和要求(不允许减小子类成员的权限)? 3、父类构造函数的初始化(子类实例化时,会自动调用父类的构造函数,如果父类构造函数带参数,必须用super(参数列表)初始化父类对象,代码写在子类构造函数的第1句) 4、this和super的用法和意义
多态性 1、多态性的定义:父类引用指向子类对象 Dialog d = new FontDialog(); 2、性质:该引用调用重写成员时,调用的是子类成员
抽象类、接口等其他内容 1、抽象类如何定义?含有抽象函数的类,类前用abstract修饰 2、抽象类有何性质?不能被实例化;可以有普通函数;里面抽象函数必须被重写(除非子类也是抽象类) 3、接口如何定义?和抽象类有何区别?interface,用implements来实现。可以实现多个接口;接口中所有的函数都是抽象函数,并且是public;所有变量都是public的静态的final的变量(常量) 4、Object类:Java中所有类的最高父类 5、final关键字:final变量、final函数、final类特点
Java语言基本功能 异常 1、try、catch、finally:匹配规则try(1)-catch(1..)-finally(0,1);程序运行的逻辑;了解Java中常见异常类和异常原因的对应(代码预测):NullPointerException、NumberFormatException、IOException等等 2、throw、throws:掌握其使用的位置
多线程 1、定义线程(能写代码):继承Thread、实现Runnable Thread.sleep(XX);知道其用法 2、调用线程:启动线程:继承Thread、实现Runnable两类有不同的启动方法 3、控制线程:暂停和继续
基本API 1、java.lang:Math重点掌握随机数;String重点掌握字符串长度、字符串按位置访问函数;StringBuffer掌握和String的区别;基本数据类型的包装类掌握字符串和数值的互相转换 以上需要会编程 2、java.util:变长数组:List系列(子类)、Set系列(子类)、Map系列(子类);掌握其区别;需要能够用它们解决实际问题(写伪代码)
IO 1、java.io包 2、File、FileInputStream、FileOutputStream、BufferedReader、PrintStream、RandomAccessFile各自的功能(不编程)
Java语言界面开发 界面和控件 javax.swing: 界面和控件,常见的必须知道意义 java.awt:颜色和字体
布局 java.awt: FlowLayout/BorderLayout/GridLayout区别
Java事件开发 事件开发 java.awt.event包,按钮点击等简单事件用什么来监听,发出什么类型事件?
Java绘图 Graphics、paint函数作用;repaint函数作用
Java网络编程 基本概念 IP地址、端口各自区别;服务器、客户端的区别
具体实现 java.net ServerSocket\Socket各自作用;ServerSocket的accept函数的意义。

文章作者: ShiQuLiZhi
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 ShiQuLiZhi !
评论
  目录