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循环的数组元素遍历

java
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
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

java
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.

java
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();

java
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 练习

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

java
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);来实现。

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

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

java
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】软件登录之后,用户可能会打开很多不同界面,这些界面如何记住登录用户的用户名,以便载入相应内容?

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

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

java
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

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

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

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

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

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

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


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

读取或给成员变量赋值:

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

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

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

java
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 父类{}

java
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不能在本项目中使用。

java
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(参数)表示调用自己的构造函数(用于构造函数互相调用的情况,比如如下代码)
java
class Customer{
    Customer(){
        this(3,5); 
    }
    Customer(int a,int b){}
}

3.2.2.2 本质理解

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

继承的作用是代码重用

java
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)最强:继承【血缘关系】

java
class Dialog{}
class FontDialog extends Dialog{}

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

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

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

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

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

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

java
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类中的函数语句段显示。

java
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++中的纯虚函数)。
  • 抽象类不能被实例化。

【注意】

  • 抽象类中可以有普通函数,则抽象类可以拥有一些自己的功能。抽象类可以写构造函数
  • 抽象类不能被实例化,但它的构造函数可以在子类中初始化某些变量,普通函数可以被子类重用;
  • 抽象类、抽象函数是一种标准化手段。
java
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,…{}
  • 接口起到连接作用,有时也叫“界面”。
java
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方法,可以给子类一个以字符串展示的机会。

    java
    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();“。

    java
    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));
        }
    }

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

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

java
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按顺序,随机找两个打乱,打乱多次
java
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走人,其中输光退场。

java
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,但若两个字符串赋值方式不同,用”==“判断两个字符串是否相等,结果将存在问题:

    java
    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

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

java
//因为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:底层用数组存储,实现了多线程的安全

java
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值,后面的将会把前面的覆盖。

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

java
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 拓展应用

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

java
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块确保了程序的安全性。

    java
    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一定会执行,从底层确保了需要执行的代码一定会执行(安全性)

    java
    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之间,返回年龄本身数值;否则返回“年龄范围错误”。

java
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标记。
java
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方法。
java
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函数
	}
}

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

java
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方法。
java
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秒后继续下载,编写代码实现该功能。

java
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)。

java
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
    这种方式。实际上,是将线程功能进行了退化,“几乎又变成串行”。
java
//此处不能用方法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.

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

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

java
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下面的所有文件.

java
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]编写一个病毒,用户运行,自动清空输入文件夹路径中所有的文件和文件夹。

java
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中推出专门支持字符流的文件输入输出类。

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();
	}
}

批量读取

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);
		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配套

java
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参数确定。

java
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中。

java
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。

java
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

用框架界面

java
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);
	}
}

通过构造函数进行初始化

java
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

举一反三:

  • 知道一个控件,用哪个类?
  • 查文档看构造函数和其他重要函数
  • 将控件实例化后加到容器中
java
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

    java
    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(图标)

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

    java
    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函数设置布局。

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

java
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个按钮在界面上。

java
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国际象棋棋盘。

java
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
image-20220419112326206

java
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]设置一个按钮,从界面上方以抛物线的形式掉下来。

java
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]小球自由落体掉下,弹起来,再次自由落体。

java
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接口
  • 将事件响应的代码,放在接口中重写的函数
  • 绑定,确保按钮发出命令,响应对象能够执行

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

java
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();
	}
}

更加简洁的方法是:

java
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参数表示命令发出时,封装的命令发出方的信息

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

java
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==:监听选项变化时要响应的事件,如下拉菜单等

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

java
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]鼠标进入按钮,按钮变红,退出,按钮变白。

java
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]界面上有一个图片,鼠标进入,图片随机躲开。

java
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==:监听鼠标移动和拖动操作

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

java
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==:监听键盘输入时要响应的事件(如俄罗斯方块)

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

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

java
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();
	}
}

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

java
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
    image-20220420230534660

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

8.7 Java绘图

8.7.1 绘制基本图形

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

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

预备知识:

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

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

基础演示:

java
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]界面上,不断随机位置出现随机颜色的线段。

java
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]界面上,鼠标拖动,可以连续画线。

java
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)

基础演示:

java
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();
	}
}

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

java
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) 
    

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

java
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坐标
java
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();
	}
}

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

java
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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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 !
评论
来发评论吧~
Powered By Valine
v1.5.2
  目录
  1. ch1.Java基础
    1. 1.1 Java特点
    2. 1.2 变量和数据类型
    3. 1.3 基本运算
    4. 1.4 数组
  2. ch2.面向对象编程的基本概念
  3. ch3.面向对象的基本特征
  4. ch4.Java语言核心包
  5. ch5.Java异常处理
  6. ch6.Java多线程开发
  7. ch7.JavaIO操作
  8. ch8.Java界面开发与事件处理
  9. ch9.Java网络编程
  10. 附:JavaSE思维导图