Java随堂笔记

Java概述

一、JDK安装及环境变量配置

1.JDK

1.1 下载JDK

1、进入JDK下载地址,选择自己要下载的JDK版本,会出现下载界面,根据自己的操作系统选择下载对应的版本即可。因为旧版本相对于新版本可能稳定一些,本次安装采用JDK SE8下载安装,按照图2的红色箭头点击下载。
图1
在这里插入图片描述

1.2 安装JDK

在这里插入图片描述
点击上图中箭头所指的地方,会出现下面的这个界面,此时你需要根据你的电脑系统来进行对应的版本进行选择,在选择版本和下载之前你需要首先接收协议,具体界面如下图所示:
1、双击以后进行JDK的安装
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.3 环境变量配置

在安装完成后,需要进行环境变量的配置,首先右键我的电脑—属性----高级系统设置就会看到下面的界面:
在这里插入图片描述
1、点击上图中的环境变量,然后开始环境变量的配置:
(1)点击系统变量下面的新建按钮,变量名JAVA_HOME(代表你的JDK安装路径),值对应的是你的JDK的安装路径。
在这里插入图片描述
(2)继续在系统变量里面新建一个CLASSPATH变量,其变量值如下图所示:
在这里插入图片描述
备注:jdk环境变量的配置流程
1.打开我的电脑–属性–高级–环境变量

2.新建系统变量JAVA_HOME 和CLASSPATH
变量名:JAVA_HOME
变量值:C:\Program Files (x86)\Java\jdk1.8.0_181
变量名:CLASSPATH
变量值:.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;

3.选择“系统变量”中变量名为“Path”的环境变量,双击该变量,把JDK安装路径中bin目录的绝对路径,添加到Path变量的值中,并使用半角的分号和已有的路径进行分隔。
变量名:Path
变量值:%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
这是java的环境配置。

1.4 JDK安装测试

1、win+R,输入cmd,进入命令行界面,如下所示:
在这里插入图片描述
2、输入java -version命令,可以出现如下图的提示,你可以看你安装的JDK版本。
在这里插入图片描述
3、输入java命令就会出现如下图所示的结果
在这里插入图片描述

4、输入javac命令可以出现如下的提示:
在这里插入图片描述

2、第一个Java程序

2.1用记事本编写程序

(1)打开记事本
(2)输入书本第9页示例程序

public class MyFirstDemo{
       public static void main(String args[]){
         System.out.println("This is my java");
         }
       }

(3)将文件保存为MyFirstDemo.java(文件名和类名要保持一致),文档的后缀名是.java

(4)win+R,输入cmd,进入命令行界面,
1、输入javac MyFirstDemo.java
2、输入Java MyFirstDemo
结果为This is my java!
备注:
测试流程:
1.首先创建一个记事本,将保存的helloworld代码写入。
2.将文件后缀改为.java。
3.用windows+r 打开“运行”窗口,输入cmd。
4.在打开界面输入cd+文件位置。
5.然后输入 javac+文件名包括后缀。
6.再输入java+文件名即可。

2.2 用开发工具IntelliJ IDEA测试编写程序

在这里插入图片描述

2.3 命令总结

1、dir:目录(查看当前文件)
2、cd:用于切换当前工作目录

二、Java基础

1、Java中的标识符

标识符:就是给类,方法,变量等起名字的符号
标识符是Java对包、类、方法、参数和变量的命名,在命名时需要遵守以下的规则。

  1. 标识符包括字母、数字、下画线”_"、美元符号“ $"。
  2. 标识符必须以字母、下画线”_"、美元符号“ $".开头,不能以数字开头
  3. 标识符不能使用Java中的关键字。
    表示类名的标识符:每个单词的首字母大写,如Man
  4. 表示方法和变量的标识符:
    小驼峰命名法:(针对方法、变量命名)
    当标识符是一个单词的时候,首字母小写,eg:name
    当标识符由多个单词组成的时候,第一个单词首字母小写,其他单词首字母大写(驼峰原则),如eatFood,firstName
    大驼峰命名法:(针对类来命名)

注意:
1.当标识符是一个单词的时候,首字母大写,eg:Student
2.当标识符由多个单词组成的时候,每个单词的首字母大写
3.关键字不能用作标识符
eg:合法标识符举例:age、$salary、_value、__1_value
非法标识符举例:123abc、-salary,#theima

2、Java中的关键字

2.1.关键字概述

关键字:就是被Java语言赋予了特定含义的单词

2.1.关键字特点

1、关键字的字母全部小写
2、常用的代码编辑器,针对关键字有特殊的颜色标记,非常直观。

Java关键字是java保留内部使用的特定单词符号,不能作为用户取名的标识符
(这些保留字不能用于常量、变量、和任何标识符的名称)
在这里插入图片描述
在这里插入图片描述

3、变量和常量

3.1.变量概述

1、变量是程序中数据的临时存放场所,变量包含的内容被称为变量的值,变量名称指代的就是对应的存储空间,通过操作变量实现对相应存储空间的操作。
eg:int a表示变量a可以存储整数,空间大小为4字节
2、变量作为程序中最基本的存储单元,在使用前必须声明,声明时需要清楚数据类型、变量名和作用域。
eg:int a=10;int a; a=10;//声明变量a,并给a进行赋初始值10
3、在程序运行过程中,其值可以发生改变的量,从本质
注意:变量名称必须是合法的标识符,变量声明是一条完整的语句,因此声明变量的语句必须以分号结束。

3.2.常量概述

1、常量是指在程序执行过程中始终保持不变的量,常量初始化后就不能够被改变。
2、Java用关键字final来定义常量,常量的声明格式为:

final type varName =value;
final double PI=3.14;//声明一共浮点型常量PI,并且初始化值为3.14
PI=3.15;//编译错误,常量的值不能够被改变
3.2.1常量分类

常量类型
1.字符串常量,用双引号括起来的内容,eg:“HelloWorld”
2、整数常量,不带小数的数字,eg:666,-88
3、小数常量:带小数的数字。13.14.-5.21
4、字符常量:用单引号括起来的内容,eg:‘A’ ‘0’
5、布尔常量:布尔值,表示真假,只有两个值:true、false
6、空常量:一个特殊的值,空置,值是null
举例说明

public class demo1 {
    public static void main(String[]args){
        //字符串常量
        System.out.println("Hellowold");
        //整数常量
        System.out.println(666);
        System.out.println(-666);
        System.out.println("--------------------");
        //小数常量
        System.out.println(3.14);
        System.out.println(-5.14);
        System.out.println("--------------------");
        //字符常量
        System.out.println('A');
        System.out.println('0');
        System.out.println('我');
        System.out.println("--------------------");
        //布尔常量
        System.out.println(true);
        System.out.println(false);
        System.out.println("--------------------");
        //空常量
        //空常量是不能直接输出的
        //System.out.println(null);报错
    }
}

3.3.Java中的数据类型

3.3.1 计算机存储单元

我们计算机是可以用来存储数据的,但是无论是内存还是硬盘,计算机存储设备的最小信息单元叫**“位(bit)”,我们又称之为“比特位”,通常用小写的字母“b”表示。而计算机中最小的存储单元叫“字节(byte)”,通常用大写字母“B"**表示,字节是由连续的8个位组成。
除了字节外还有一些常用的存储单位,
1B(字节)=8 bit
1 KB =1024 B
1 MB = 1024 KB
1 GB = 1024 MB
1 TB = 1024 GB

3.3.2 分类

Java语言是强类型语言,对于每一种数据都给出了明确的数据类型,不同的数据类型也分配了不同的内存空间,所以它们表示的数据大小也是不一样的。
数据类型是程序语言构成要素中相当重要部分。Java的数据类型可以分为基本数据类型和引用数据类型。

数据类型的分类如图所示:
在这里插入图片描述
整型用于表示没有小数部分的数值,可以表示负数。Java中提供四种整型数据类型,分别为byte、short、int和long。这四种数据类型的区别在于占用存储空间的大小不同,所能够表示的整数的范围不同。数据类型如图所示:
在这里插入图片描述
整数数据类型:byte、short、int(默认)、long
浮点数数据类型:float、double(默认)
字符数据类型:char
布尔数据类型:boolean

数据类型名称 /取值范围
byte(1字节)字节型/(-128~127)
short(2字节)短整型
int(4字节)整型,取值范围(-21亿~21亿)
long(8字节)长整型
float(4字节)单精度浮点类型
double(8字节)双精度浮点类型
char(2字节)char类型的值用单引号括起来
boolean(4字节)布尔型的值只能是 true 或 false

示例:
1、整型变量
1.基本格式:int 变量名 = 初始值;
代码示例:


public  class Test {
public static void main(String[] args) {
          int a=10;
      System.out.println(a); }
}

int i = 12345678901; //	报错,12345678901 超出int类型的取值范围

当声明long类型的常量需要在数值的后面加上“L”。
在这里插入图片描述
在这里插入图片描述
注意:

          int a=10;
      System.out.println("a"); //把括号的值原样输出,结果为a
      System.out.println(a); //输出a的值,那么a的值为10

2、浮点型
在Java中带小数的数据用浮点型表示,浮点型可以分为单精度浮点型(float)和双精度浮点型(double),float类型的数据的小数点后可以精确到7位有效数字,有些时候float是无法满足精度需求的,这时候需要double(双精度型),double类型的数据的精度约是float类型的两倍**,因此Java中小数的默认数据类型为double类型**。如果声明float类型,则需要在数据后面加“F”或“f”,如果没有加,确认数据为double类型

         float f = 3.14F;
         float d =3.14;

3、字符型
1、字符型即char类型数据在内存中占2字节。Java中使用单引号引起来以表示字符常量,如‘A’表示A是一共字符型数据。Java中还可以将""作为转义字符将其后的字符转变为其他含义,常用的转义字符及其含义如下图所示:

符号字符含义
\n换行 (0x0a)
\r$12
\f换页符(0x0c)
\b退格 (0x08)
\0空字符 (0x0)
\s空格 (0x20)
\t制表符
\"双引号
\’单引号
\\反斜杠

转义字符举例如下:

    char ec='a';
    char cc='中';
    char c='\n';//代表换行符
public class Test{
    public static void main(String []args){
        char a='\u2764';
        char c='\u2765';
        System.out.println(a);
        System.out.println(c);
    }
}

输出结果为:
在这里插入图片描述
2、字符型数据只能存放一个字符,如果存放多个字符的数据则不能使用char类型,应该使用String类型,String类型不属于基本数据类型,需要用双引号引起来,String类型适用于程序设计,例如:

    public class Test{
    public static void main(String []args){
        String a="abcd";
        String c="java程序设计";
        System.out.println(a);
        System.out.println(c);
    }
}

在这里插入图片描述
3、字符串连接符(+)
a、一种是运算的加法
b、一种是字符串的连接符

public class Test{
    public static void main(String []args){
        int a=2;
        int b=3;
        System.out.println("a"+b);//输出结果为a3
        System.out.println(a+b);//输出结果为5
        //System.out.println(1.0/3);
    }
}
        System.out.println(1/3); //输出结果为0
        System.out.println(1.0/3);//输出结果为0.3333333333333333

默认为整数型

4、布尔型
布尔型数据只有两个常量,true和false,不可以用0或非0来表示true和false,布尔类型通常用来判断条件,用于程序流程控制,逻辑条件的判断,真和假默认值是 false,例如:

    boolean flag;//定义一个布尔类型变量flag
    flag=true;//给flag赋初值为true

3.4 类型转换

3.4.1 自动类型转换

把一个表示数据范围小的数值或者变量赋值给另一个表示数据范围大的变量
1、整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。
2、转换从低级到高级。
低 ------------------------------------> 高
byte,short,char—> int —> long—> float —> double
在这里插入图片描述
实线箭头表示无信息丢失的数据类型转换;虚线箭头表示可能在转换时,出现精度丢失。
在这里插入图片描述

3.4.2 强制类型转换

把一个表示数据范大的数值变量赋值给另一个数据范围小的变量
1、格式:目标数据类型 变量名=(目标数据类型)值或者变量;
eg:int k =(int)88.88;

/*
类型转换
 */
public class demo2 {
    public static void main(String[] args) {
        //自动类型转换
        double d =10;
        System.out.println(d);
        //定义byte类型的变量
        byte b=10;
        short a=b;
        int i=b;
        //这是不可以的,类型不兼容
        //char c=b;

        //强制类型转换
        int k= (int) 88.88;
        System.out.println(k);
    }
}
public class Test{
    public static void main(String []args){
        short a,b;
        a=32767;
        b=(short)(a+2);
        System.out.println(a);//输出结果为32767
        System.out.println(b);//输出结果为-32767
    }
}

4、数据类型转换必须满足如下规则:

  1. 不能对boolean类型进行类型转换。

  2. 不能把对象类型转换成不相关类的对象。

  3. 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。

  4. 转换过程中可能导致溢出或损失精度,例如:上面的32767

  5. 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如:

b=(int)23.7//b为23
c=(int)-45.89f //c为-45
public class Test{
    public static void main(String []args){
                int x;
                double y;
                x = (int)34.56 + (int)11.2;  // 丢失精度
                y = (double)x + (double)10 + 1;  // 提高精度
                System.out.println("x=" + x);//x=45
                System.out.println("y=" + y);//y=56.0
            }
        }

备注:
1、 整数的默认类型是 int。
2. 小数默认是 double 类型浮点型,在定义 float 类型时必须在数字后面跟上 F 或者 f。

3.5 变量的作用域

变量需要在它的作用范围内才可以被使用,作用范围称为变量的作用域。例如:
在这里插入图片描述输出结果为:
在这里插入图片描述
如果在a的作用域输出b的值,就会出现报错,找不到变量b

4、注释

4.1.注释概述

注释是在程序指定位置添加的说明性信息
注释不参与程序运行,仅起到说明作用

4.2注释分类

1、单行注释
格式://注释信息
2、多行注释
格式:/注释信息/
3、文档注释
格式:/*注释信息/

/*  注释不参与运行
多行注释
Java程序中最基本的组成单位是类。
类的定义格式:
public class 类名{
}
这是我定义的HelloWorld类
 */
public class HelloWorld {
    /*
        这是main方法
        main方法是程序入口方法,代码执行时从main方法开始的
     */
    public static void main(String[] args) {
        //单行注释
        //这是输出语句,“ ”里面的内容时可以改变的
        System.out.println("hello word");
    }
}

5、运算符和表达式

1、运算符和表达式是构成程序语句的要素,Java提供了一组丰富的运算符来进行不同的运算处理。表达式是由操作数(常量和变量)和运算符按一定的语法形式组成的符号序列。
2、运算符:对常量或者变量进行操作的符号
表达式:用运算符把常量或者变量连接起来符合java语法的式子就可以称为表达式。不同运算符连接的表达式体现的是不同类型的表达式。
举例说明:
int a=10;
int b=20;
int c=a+b;
+:是运算符,并且是算术运算符
a+b:是表达式,由于+是算术运算符,所有这个表达式叫算术表达式

5.1 算术运算符

在这里插入图片描述
注意事项:
/% 的区别:两个数据做除法,/ 取结果的%取结果的余数
整数操作只能得到整数,要想得到小数,必须有浮点数参与运算。

public class test01 {
    public static void main(String []args){
        int a=10;
        int b=20;
        int c=25;
        int d=25;
        System.out.println("a+b="+(a+b));//a+b=30
        System.out.println("a-b="+(a-b));//a-b=-10
        System.out.println("a*b="+(a*b));//a*b=200
        System.out.println("b/a="+(b/a));//b/a=2
        System.out.println("b%a="+(b%a));//b%a=0
        System.out.println("c%a="+(c%a));//c%a=5
        //自增量++ --
        System.out.println("a++="+(a++));//a++=10
        System.out.println("a--="+(a--));//a--=11
        //自增量d++ ++d
        System.out.println("d++="+(d++));//d++=25
        System.out.println("++d="+(++d));//++d=27
    }
}

++和- -是一元运算符,只需要一个操作数,其功能分别为自身加1或减1。它可以分为前置运算和后置运算,前置运算时运算符放在操作数前面,如++i、–i;后置运算时运算符放在操作数后面,如i++、i–,但两者的区别是给其他变量赋值时的顺序不一样。例如:

int a=5;
int b=a++;//++在后,先把a的值赋值给b,a再自增1,执行后a=6,b=5
int c=++a;//++在前,a先自增1,再把a的值赋值给c,执行后a=7,c=7
public class test01 {
    public static void main(String []args){
    //a为正值时,b的值无论正负都不受影响
        int a=10;
        int b=-3;
        int c;
        c=a%b;
        System.out.println(c);//输出结果为1
        //如果a为负值的话,结果为负值
        int a=-10;
        int b=3;
        int c;
        c=a%b;
        System.out.println(c);//输出结果为-1
        //
        int a=10;
        double b=3.45;
        System.out.println(a%b);//输出结果为3.0999999999999996
    }
}
5.1.1 字符的+操作

1、拿字符在计算机底层对应的数值来进行计算的
‘A’——>65, A—Z是连续的
’a‘——>97, a—z是连续的
’0‘——>48, 0-9是连续的
2、算术表达式中包含多个基本数据类型的值的时候,整个算术表达式的类型会自动进行提升。
提升规则:
1.byte类型,short类型和char类型将提升到int类型
2、整个表达式的类型自动提升到表达式中最高等级操作数同样的类型

/*
字符的“+”操作
 */
public class demo2 {
    public static void main(String[] args) {
    //定义两个变量
        int i=10;
        char c='A'; //字符‘A’的值是65
        c='a';//字符’a‘的值是97
        c='0';//字符'0'的值是48
        System.out.println(i+c);
    }
}

5.1.2 字符串的“+”操作

1、字符串的+:拼接操作

System.out.println("I"+"helloworld");//结果为ihelloworld

2、当“+”操作中出现字符串时,这个“+”是字符串连接符,而不是算术运算。

System.out.println("hello"+6+66);//结果为hello666

3、在“+”操作中,如果出现了字符串,就是连接运算符,否则就是算术运算。当连续进行“+”操作时,从左到右逐个执行。

System.out.println(1+99+"hello");//结果为100hello

5.2 赋值运算符

5.2.1 赋值运算符

举例:

1、计算矩形的面积、圆形的面积

public class test02 {
    public static void main(String[]args){
        //1、计算矩形的面积、圆形的面积
        //定义变量:矩形面积S,长为a,宽为b
        int S;
        int a=10;
        int b=20;
        S=a*b;
        System.out.println("矩形的面积S="+S);//矩形的面积S=200

        //定义常量PI=3.14
        //定义变量:圆的面积D,半径r
        final double PI=3.14;
        double D;
        int r=2;
        D=r*r*PI;
        System.out.println("圆的面积D="+D);//圆的面积D=12.56
    }
}

2、求算经过时间t物体的自由落体位移

import java.util.Scanner;
public class test03 {
    public static void main(String[]args){
        //自由落体位移公式为s=1/2*g*t*t,
        // 其中:s(位移(m))t(时间(s))g(重力加速度(9.8m/s2)
        //求算经过时间t物体的自由落体位移
        double g = 9.8;
        double s;
        int t;
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入时间:"); //请输入时间:6
        t=scanner.nextInt();
        s = 0.5 * g*t*t;//1/2输入是不对的
        System.out.println("自由落体"+t+"秒位移是"+s);//自由落体6秒位移是176.4
    }

}

3、一个人一生中要走的路加起来可以绕地球七十五圈,地球的平均半径为6371.004干米,而一个人走一步的步长大约是60厘米。请计算一个人一生中要走多少步?

public class test04 {
    public static void main(String[]args){
        //一个人一生中要走的路加起来可以绕地球七十五圈,
        // 地球的平均半径为6371.004干米,
        // 而一个人走一步的步长大约是60厘米。请计算一个人一生中要走多少步?
        int radius = 6371004; // 地球半径
        double girth = Math.PI * radius * 2;
        long totalLength = (long) (girth * 75);
        System.out.println("人一生中所走的距离约为" + totalLength + "米。");
        //人一生中所走的距离约为3002264904米。
        double stepLength = 0.6;
        System.out.println("人走一步的长度大约为" + stepLength + "米。");
        //人走一步的长度大约为0.6米。
        long stepCount = (long) (girth * 75 / stepLength);
        System.out.println("一共需要走" + stepCount + "步。");//一共需要走5003774840步。
   }
}

4、将三位数的百位、十位、个位分离出来

import java.util.Scanner;
public class test05 {
    public static void main(String[]args){
        int a,b,c,s;
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个三位数:");//请输入一个三位数:123
        s=scanner.nextInt();
        a=s/100;//百位
        b=(s/10)%10;//十位
        c=s%10;//个位
        System.out.println("该三位数百位为"+a+"十位为"+b+"个位为"+c);//该三位数百位为1十位为2个位为3
    }
}

5.3 运算符之关系运算符

在这里插入图片描述
赋值运算符=以及和双目运算符结合的+=、-=、*=、/=、%=
应用

public class test06 {
    public static void main(String[]args){
        //两个数互换(三个变量)
        int a=10;
        int b=100;
        int c;
        c=a;//把a的值赋给c
        a=b;//把b的值赋给a
        b=c;//把c的值赋给b
        System.out.println(a);//a=100
        System.out.println(b);//b=10

        //两个数互换(两个变量)
        int d=10;
        int e=100;

        d=d+e;//d=10+100=110
        e=d-e;//e=110-10=100
        d=d-e;
        System.out.println(d);//d=100
        System.out.println(e);//e=10
    }
}

5.2 运算符之逻辑运算符

在这里插入图片描述

在这里插入图片描述
逻辑运算符举例如下:

int a=52,b=60;
double x=5.5,y=10.2;
boolean b1=(a==b)||(x>y) //b1=flase
boolean b2=(x<y)&&(a!=b)//b2=true

5.4 运算符之条件运算符和条件表达式

条件运算符(?:)是三元运算符,由条件运算符组成的条件表达式语法格式如下:
逻辑(关系)表达式?表达式1:表达式2
条件运算符的功能是:如果逻辑(关系)表达式的值为true,则取表达式1的值;否则,取表达式2的值。条件运算符用于对常用语的简单分支进行处理。条件运算符举例如下:

max=(a>b)?a:b  //max取a和b中较大的值

简单运用

public class test07 {
    public static void main(String[]args){
        int a=7;
        int b=9;
        int max;
        int min;
        //max中取a和b中较大的值
        max=(a>b)?a:b;
        // //max中取a和b中较小的值
        min=(a<b)?a:b;
        System.out.println(max);//9
        System.out.println(min);//7
        //找出3个数值中最大值
        int max1,x=20,y=30,z=40;
        max1=(x>y)?((x>z)?x:z):((y>z)?y:z);
        System.out.println(max1);//40
    }
}

复杂运用
1、该年是否为闰年(四年一闰,百年不闰,四百年再闰

import java.util.Scanner;
public class test08 {
    public static void main(String[]args){
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入年份(例如:2012)"); //请输入年份:2018
        int year = scanner.nextInt();
        //普通闰年:公历年份是4的倍数的,且不是100的倍数,为普通闰年。
        boolean isLeapYear = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
        String msg = isLeapYear ? year + "是闰年" : year + "不是闰年";
        System.out.println(msg); //2018不是闰年
    }
}

5.5 运算符之圆括号

圆括号运算符“()”用于改变表达式中该运算符的运算次序。先进行括号内的运算,再进行括号外的运算;在有多层括号的情况下,优先进行最内层括号内的运算,再依次从内向外逐层运算。

综合练习:

一、Double、长整型、运算符
1、计算牛郎星到织女星的距离。牛郎星到织女星的距离为16.4光年, 如果一只喜鹊的长度是046米,计算
一下牛郎织女真的要会面需要动用多少只喜鹊。
注:光速:299792458米/秒,1光年=(13652460光速/1000)公里,1公里=1干米

public class test12 {
    public static void main(String[] args) {
        double distance=16.4;//牛郎星到织女星的距离为16.4光年
        int speedoflight=299792458;//光速:299792458米/秒
        long d=(long)(distance*365*24*60*60*speedoflight/1000);
        System.out.println("牛郎星到织女星是"+distance+"光年,合"+d+"公里。");
        double dMagpie=0.46;
        System.out.println("一只成年喜鹊的长度是"+dMagpie *100+"厘米");
        long numberOfMagpie=(long)(d * 1000/dMagpie);
        System.out.println("搭起鹊桥需要"+numberOfMagpie+"只喜鹊");
    }
}

在这里插入图片描述
2、等额本息还款公式:payment=(pr(1+r)m/((1+r)m-1),其中: p (贷款本金(元) r (贷款月利率, m (还款月数);根据指定的贷款本金,贷款月利率和还款年数, 系统根据上述信息计算每月还款金
额。【扩展:Math . pow ( double a, double b )】

import java.util.Scanner;
public class test10 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        System.out.println("请输入你的贷款本金:(万)");
        double p=scanner.nextDouble()*10000;
        System.out.println("请输入你的贷款年利率:(%)");
        double r=scanner.nextDouble()/1200;
        System.out.println("请输入你的还款年数:");
        int m = scanner.nextInt()*12; //使用int类型定义还款年数
        double payment=(p * r * Math.pow((1+r),m)/(Math.pow(1+r,m)-1));//系统系统根据等额本息还款公式计算出还款情况,使用Math.pow(double a,double b)计算幂运算,该方法返回a的b次幂的值
      payment=Math.round(payment*100)/100.0; //math.round()方法确保还款金额保留两位小数
      System.out.println("你的每月还款金额为:¥"+payment);
    }
}

在这里插入图片描述
等额本金还款公式为 :每月还款额=贷款本金/贷款期月数+(本金-已归还本金累计额)×月利率
比如,贷款金额(即贷款本金)为30万元,贷款年利率为 6.14%,还款年数为 20年,则计算方式如下:

import java.util.Scanner;
//计算使用等额本金方式还贷的情况。用户从控制台输入贷款本金,贷款月利率和还款年数,系统根据上述信息计算第一个月和第二个月的还款金额,要求每月还款金额保留两位小数并且输出到控制台。
public class test11 {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你的贷款本金:(万)");
        double p = scanner.nextDouble() * 10000;
        System.out.println("请输入你的贷款年利率:(%)");
        double r = scanner.nextDouble() / 1200;
        System.out.println("请输入你的还款年数:");
        int m = scanner.nextInt() * 12;
        scanner.close();
        double firstPayment = p / m + p * r;
        firstPayment = Math.round(firstPayment * 100) / 100.0;
        System.out.println("你的第一个月的还款金额为:¥"+ firstPayment);
        double secondPayment=p/m+(p-p/m)*r;
        secondPayment=Math.round(secondPayment*100)/100.0;
        System.out.println("你的第二个月的还款金额为:¥"+ secondPayment);
    }

}

每月本金:300000/240 = 1250
月利率:6.14%/12
首月还款:1250 + 300000 * 6.14%/12 = 2785
第 2 月还款: 1250 + (300000 - 1250) * 6.14%/12 = 2778.60
在这里插入图片描述

三、Java程序控制结构

1、三种基本控制结构:顺序结构、分支结构和循环结构
2、常用流程图符号如图所示:
在这里插入图片描述

1. 顺序结构

1、顺序结构是问题描述中简单常用的一种结构,即按照算法的流程,自上而下,依次执行。
在这里插入图片描述

import java.util.Scanner;
public class test13 {
    public static void main(String[] args) {
        double a;//a为摄氏温度
        double b;//b为华氏温度
        Scanner scanner=new Scanner(System.in);
        System.out.println("请输入摄氏温度:"); //35
        a = scanner.nextInt();
        b=a*(9/5)+32;
        System.out.println(b);//67.0
    }
}

2 .分支结构

1、分支结构又称为选择结构,此种结构在处理问题时需要根据条件进行判断。根据要处理的分支不同,分支结构可分为单分支结构和多分支结构。分支结构流程图如图所示:
在这里插入图片描述
在这里插入图片描述
2、先进行条件判断后执行
关注点:进入点:关键字if switch
判定条件(逻辑值,如关系表达式等)
判定后执行语句(尤其是注意空语句)
易错点:括号配对

2.1 if语句(单分支)

1、示例
if…else…
如果。。。否则。。。
2、操作步骤:
输入if基本结构
if(true){
;
}else{
;}

3、判断条件
逻辑表达式
逻辑值

4、练习

import java.util.Scanner;
public class test14 {
    //TODO 根据身份证号码第17位数输出是否性别。如果是奇数,输出是男生,否则,输出是女生
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        System.out.println("请输入你的身份证号码第17位数:");  //1是男生,2是女生
         int a = scanner.nextInt();
         if(a%2==0){
             System.out.println("女生");
         }else{
             System.out.println("男生"); 
         }
    }
}

public class test15 {
    public static void main(String[] args) {
        //TODO示例:输出两个数中最大的数,如果a大,输出a,否则输出b
        int a, b;
        a = 3;
        b = 4;
        if (a > b) {
            System.out.println(a);
        } else {
            System.out.println(b);
        }
    }
}

2.2 if语句(多分支)

1、序列
if…else if…else if…else…
如果。。。 否则 如果。。。。
2、操作步骤
输入if基本语句结构
if(true){
;
}else if(true){;
}else{ ; }

3、Scanner的使用
请一位负责输入的管家

Scanner scanner=new Scanner.(System.in);
//管家 张三=新青睐(开辟内存空间,和谁对接);

管家张三等待系统输入scanner.nextInt()并处理:

int a=scanner.nextInt();

4、练习
判断成绩对应的等次
【90-100】优秀
【80-90】良好
【70-80】中等
【60-70】及格
【0-60】不及格

import java.util.Scanner;
public class test16 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你的成绩:");
        int a = scanner.nextInt();
        if (a < 60) {
            System.out.println("不及格");
        } else if (a < 70) {
            System.out.println("及格");
        }
            else if (a<80){
            System.out.println("良好");

            }
            else if(a<=100){
            System.out.println("优秀");

        }
    }
}

个人所得税
计算个人所得税的缴纳情况。用户从控制台输入税前工资的金额,系统根据用户输入的工资金额计算应缴纳的税额,如图所示:
在这里插入图片描述
注:
工资个税的计算公式为:
应纳税额=(工资薪金所得-扣除数)×适用税率-速算扣除数
全月应纳税所得额=月工资薪金所得-扣除数
2011年 9月1日起执行7级超额累进税率:扣除数为3500元。

import java.util.Scanner;
public class test17 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你的工资的税前金额(¥):");
        double salary = scanner.nextDouble();
        double tax = 0.0;
        double taxIncome = salary-3500;
        if(taxIncome<=0) {
            tax = 0.0;
        }else if(taxIncome<=1500) {
            tax = taxIncome*0.03;
        }else if(taxIncome<=4500) {
            tax = taxIncome*0.10-105;
        }else if(taxIncome<=9000) {
            tax = taxIncome*0.20-555;
        }else if(taxIncome<=35000) {
            tax = taxIncome*0.25-1005;
        }else if(taxIncome<=55000) {
            tax = taxIncome*0.30-2755;
        }else if(taxIncome<=80000) {
            tax = taxIncome*0.35-5505;
        }else {
            tax=taxIncome*0.45-13505;
        }
        System.out.println("你应该缴纳的个人所得税是:¥"+tax);
        scanner.close();
    }
}

扩展:
计算个人工资收入的保险缴纳情况以及个人所得税。用户从控制台输入税前工资的金额,系统根据用户输入的工资金额计算应缴纳的各项保险的总金额,并计算应该缴纳的税额。

import java.util.Scanner;
  public class test18 {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        System.out.println("请输入你的工资的税前金额(¥):");
        double salary=scanner.nextDouble();
        scanner.close();
        //计算各项保险金额
        double endowmentInsurance=salary*0.08;
        double medicalInsurance=salary*0.02;
        double unemploymentInsurance=salary*0.002;
        double accumulationFund=salary*0.12;
        double insurance=endowmentInsurance+medicalInsurance+unemploymentInsurance+accumulationFund;
        System.out.println("你应该缴纳的各项保险总金额为:¥"+insurance);
        //计算个税
        double tax=0.0;
        double taxIncome=salary-insurance-3500;
        if(taxIncome<=0) {
            tax = 0.0;
        }else if(taxIncome<=1500) {
            tax = taxIncome*0.03;
        }else if(taxIncome<=4500) {
            tax = taxIncome*0.10-105;
        }else if(taxIncome<=9000) {
            tax = taxIncome*0.20-555;
        }else if(taxIncome<=35000) {
            tax = taxIncome*0.25-1005;
        }else if(taxIncome<=55000) {
            tax = taxIncome*0.30-2755;
        }else if(taxIncome<=80000) {
            tax = taxIncome*0.35-5505;
        }else {
            tax=taxIncome*0.45-13505;
        }
        System.out.println("你应该缴纳的个人所得税是:¥"+tax);
        scanner.close();
    }
}

注:
工资个税的计算公式为:
应纳税额=(工资薪金所得-各项保险金额-扣除数)×适用税率-速算扣除数
全月应纳税所得额=月工资薪金所得-各项保险金额-扣除数
其中,各项保险金额的缴纳比例如下:
养老保险:月工资薪金所得 × 8%
医疗保险:月工资薪金所得 × 2%
失业保险:月工资薪金所得 × 0.2%
公积金:月工资薪金所得 × 12%

2.3 switch语句

1、switch语句是Java中的另一种条件语句,执行过程为:先判一个变量与一系列值中的某个值是否相等,每一个值构成一个分支,如果相等,则从多条分支中选择对应相应的分支来执行。使用if分支语句也可以实现同样的效果,但是相较而言使用switch语句会使代码的可读性更强。
2、注意要点:
(1) swich语句中开始的表达式运算结构必须是char、byte、 short 或int类型。
(2)每一个case子句中的表达式必须为常量,不能为变量或其他表达式。
(3)当执行到每个case子句结束处的break语句时,会退出switch语句中对应的case子句执行。如果没有break语句,则会继续执行后面case指示的若干语句。
3、
switch() {case}
如果。。。
第一种。。。
第二种。。。
第三种。。。
4、操作步骤:
switch(key){
case value:
break;
default:break;
}
5、使用要点:
判断条件多重且为离散型、
break使用
6、练习:

public class test19 {
    public static void main(String []args){
        char a;
        a='C';
        switch (a){
            case 'A':
                System.out.println("你的成绩很优秀,在91-100之间!");
                break;
            case 'B':
                System.out.println("你的成绩很优秀,在91-100之间!");
                break;
            case 'C':
                System.out.println("你的成绩很优秀,在91-100之间!");
                break;
            case 'D':
                System.out.println("你的成绩很优秀,在91-100之间!");
                break;
            case 'E':
                System.out.println("你的成绩很优秀,在91-100之间!");
                break;
            default:
                System.out.println("是不是哪儿出了问题,我不知道要怎么处理。");
                break;
        }

    }
}

3、循环结构

1、顺序结构的程序语句只能被执行一次,如果同样的语句被执行多次,那么就需要使用循环结构。
2、Java中主要有三种主要的循环结构:while循环、do…while循环、for循环。
3、循环由循环条件和循环体组成,使用循环时要注意不要造成死循环。
4、循环三要素:循环变量的初始化、循环的条件(以循环变量为基础)、循环变量的改变(向着循环的结束变化)

3.1 while循环

while(表达式){
语句或语句块
}
while循环是基本的循环,像if语句一样先计算布尔表达式的值,当值为true时执行循环语句,循环体执行完毕后再次计算表达式的值。当表达式的值不为true时,结束while语句。
while判断为真后返回起点重新进行
关注点:
while语句的语义时:计算表达式的值,当值为真时,执行循环体语句。
易错点:
括号配对,判断条件。
在这里插入图片描述
在这里插入图片描述

/*累加求和,计算1+2+3+...+100=?
 用while实现
 */
public class test20 {
    public static void main(String[] args) {
        int i=0;
        int sum=0;
        while(i<=100){
            sum=sum+i;
            i++;
        }
        System.out.println(sum);
    }
}
public class test20 {
    public static void main(String[] args) {
    int sum=0,i=0;
        while(i<100){
            i=i+1;
            sum=sum+i;
            System.out.println("这是我的第"+i+"次累加,结果是:"+sum);
        }
    }
}

在这里插入图片描述
在这里插入图片描述

import java.util.Scanner;
public class test22 {
    public static void main(String[] args) {
        int i=0,j;
        Scanner scanner=new Scanner(System.in);

        while(i<100000) {
            System.out.println("请输入些什么【仅限数字】,如果是3,就退出");
            j = scanner.nextInt();
            if (j == 3) {
                i = 100000;
            }
            System.out.println("持续运行中");
        }
            System.out.println("已经成功退出了");
        }
    }

在这里插入图片描述
练习:
完成功能:
输入年份,显示出该年份是否闰年,每个月各有多少天
接受并显示某年份是否闰年
判断指定月份的天数
1、流程
a、循环、执行退出系统
b、交互系统(做什么,怎么做,用户看到什么,与用户进行交互)
c、闰年
d、每月天数

import java.util.Scanner;
public class test23 {
//TODO:输入年份,显示出该年份是否闰年,每个月各有多少天,接受并显示某年份是否闰年,判断指定月份的天数
//1、3、5、7、8、10、12月有31天,4、6、9、11月有30天,
//2月平年有28天,闰年有29天。
// 判断闰年的条件是:能被 4 整除但不能被 100 整除,或者能被 400 整除。
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("您好,欢迎您参加本次测试!如果你选择继续测试,请输入1,如果不选择测试,请输入0退出");
        int year, month,two=28;
        boolean bool = true;
        while (bool) {
            System.out.println("请输入你的数字:");
            int exit = scanner.nextInt();
            if (exit == 0) {
                break;
            }
            System.out.println("请您输入你要测试的年份");

            year=scanner.nextInt();
            if (year%4==0&&year%100!=0||year%400==0){
                System.out.println(year+"为闰年");
                two=29;
            }
            else{
                System.out.println(year+"为平年");
            }
            System.out.println("请您输入月份:");
            month =scanner.nextInt();
            switch(month){
                case 1:
                case 3:
                case 5:
                case 7:
                case 8:
                case 10:
                case 12:
                    System.out.println(month+"月份有31天");
                    break;
                case 2:
                    System.out.println("2月份有"+two+"天");
                    break;
                case 4:
                case 6:
                case 9:
                case 11:
                    System.out.println(month+"你输入的月份有30天");
                    break;
                default:
                {
                    System.out.println("您输入的信息有误!");
                    break;
                }
            }
            exit++;
        }
    }
}

在这里插入图片描述
总结:
1、现实问题
2、模块化(复杂问题进行简单化处理)
3、设计
4、分步骤依次进行

3.2 for循环

1、当循环次数无法确定时,最好使用while循环或者do…while循环。如果具体循环次数确定,那么通常使用for循环更合适。for循环的一般格式如下:
for(表达式1;表达式2;表达式3){
循环语句;
}
简单理解:
for(循环变量赋初值;循环条件;循环变量增量)语句
对应于
表达1;
while(表达式2)
{
语句
表达式3;
}
for循环的控制头中包含如下三个由分号隔开的部分。
(1)表达式1用于初始化,在循环过程中只执行一次;
(2)表达式2是一个布尔表达式,在执行循环体之前需要进行判断,如果其值为 true ,则继续执行循环语句;
(3)表达式3用来修改循环变量,改变循环条件。
需要注意的是, for 循环中三个表达式都可以省略,即 for(;;)这种形式。 for 循环执行过程流程图如图所示:

在这里插入图片描述
步骤1:计算机表达式1的值
步骤2:判断表达式2是否成立,如果成立,则执行步骤3;否则,执行步骤4
步骤3:执行循环语句,再执行表达式3,改变循环条件,再执行步骤2;
步骤4:结束本次for循环
注意:1、for循环中的“表达式1(循环变量赋初值)”、“表达式2(循环条件)”和“表达式3(循环变量增量)”都是选择项,即可以缺省,但“;”不能缺省。
2、表达式1省略表示不执行初始化
3、表达式2省略表示没有判定条件,不会跳出循环,进入死循环。
4、表达式3省略表示每次循环变量i不会增加,那么判断条件2会一直为真,循环无限进行,进入死循环

简单运用:
1、求1加到100的奇数和

public class test25 {
        public static void main(String[] args){

//定义并赋值
            int n = 0;
            for(int i = 1;i <= 100;i+=2){
                n += i;
            }
            System.out.println("1到100的奇数和为:" + n);
        }

    }

2、用循环语句输出“*”字符

public class test24 {
    public static void main(String[] args) {
        int n = 5;
        for (int i = 1; i <= 5; i++) {
            for (int j = 1; j <= i; j++) {
                System.out.print("* ");
            }
               System.out.println();
                }
            }
        }

3、求100以内能同时被7和9整除的数

public class test27 {
    //求100以内能同时被7和9整除的数
        public static void main(String[] args) {
            int num = 0;
            for (int i = 0; i < 100; i++) {

                if (num % 7 == 0 && num % 9 == 0) {
                    System.out.println(num);//0和63
                }
                num++;
            }
        }
    }

4、1到100能被7和9整除的数之和

public class test26 {
    public static void main(String []args){
        int sum = 0 ;
        for(int i = 1 ; i<100;i++){
            if(i%7==0&&i%9==0)
                sum += i ;
        }
        System.out.println("1到100能被7和9整除的数之和为:"+sum);//63
    }
}

5、水仙花问题
计算1000以内的所有水仙花数。用户输入0到1000以内的某个数,系统输出这个范围以内的所有水仙花数,水仙花数是指一个n位数(n≥3),它的每个位上的数字的n次幂之和等于它本身。

public class test30 {
    public static void main(String args[]) {
        int bai=0,shi=0,ge=0;

        for (int i=100;i<1000;i++){
            bai=i/100;
            shi=i/10%10;
            ge=i%10;


            //将三位数拆解成百十个位数
            if(i==Math.pow(bai,3)+Math.pow(shi,3)+Math.pow(ge,3))
            {System.out.println(i+"是一个水仙花数。");}

        }
    }

}

结果为
153是一个水仙花数。
370是一个水仙花数。
371是一个水仙花数。
407是一个水仙花数。

public class test29 {
    public static void main(String args[]) {

            for(int i=0;i<1000;i++) {
                if(isSuiXian(i)) {
                    System.out.println(i);
                }
            }
        }
        //判断一个数是否为水仙花数
        public static boolean isSuiXian(int n) {
            double sum=0;
            double mid=n;//mid用于存放数n,以便待会与sum作比较;
            while(n>0) {
                sum+=Math.pow(n%10,3);//sum进行累加各个数的3次幂
                n/=10;
            }
            if(sum==mid) {
                return true;
            }
            else {
                return false;
            }
        }
    }
    

6、求从1到5的阶乘和

package work;

public class test04 {
	public static void main(String []args) {
		int sum=0,temp=1;
		for(int i=1;i<=5;i++) {
			for(int j=1;j<=i;j++) {
				temp=temp*j;
			}
			sum=sum+temp;
			//因为temp存储是每个数的阶乘,再计算下一个数之前,需要恢复原始值1
			temp=1
			}
		System.out.println(sum);
	}

}
public class test31 {
    public static void main(String args[]) {
        //求阶乘 i从1到5
        int jiecheng =1,sum=0;
        for(int i=1;i<=5;i++)
        {
            for(int j=1;j<i;j++)
            {
                jiecheng*=j;
            }
            sum+=jiecheng;
            jiecheng=1;
        }
        System.out.println(sum);//34
    }
}

7、百钱买百鸡:公鸡5文钱一只,母鸡3文钱一只,小鸡1文钱3只,如何用百文钱买百只鸡?

package work;
//思路1:百文买百鸡:
//买公鸡i只,母鸡j只,小鸡k只(k从3开始,步长为3)i*5+j*3+k/3==100 && i+j+k==100
//执行3重循环:i 0-20 j 0-33 k 3-99
public class test05 {
	public static void main(String []args) {
		int i,j,k; //公鸡i只,母鸡j只,小鸡k只
		for(i=0;i<=20;i++) {
			for(j=0;j<=33;j++) {
				for(k=3;k<=99;k=k+3) {
					if((i*5+j*3+k/3==100) && (i+j+k==100)) {
						System.out.println("公鸡有"+i+"只"+"母鸡有"+j+"只"+"小鸡有"+k+"只");
						}
					
				}
			}
		}
		
	}

}

结果为:
公鸡有0只母鸡有25只小鸡有75只
公鸡有4只母鸡有18只小鸡有78只
公鸡有8只母鸡有11只小鸡有81只
公鸡有12只母鸡有4只小鸡有84只

public class test32 {
    /**
     * 思路2:百文买百鸡
     * 买公鸡i只,小鸡k只(k从3开始,步长为3),母鸡100-j-k只,i*5+j*3+k/3==100
     * 执行两重循环:i 0-20  k 3-99
     */
    public static void main(String args[]) {
        int i,j,k;
        for (i = 0; i <= 20; i++) {
            for (k = 3; k <= 99; k++) {
                if (i * 5 + j * 3 + k/3 == 100) {
                    j=100-i-k;
                    System.out.println("公鸡有" + i + "只" + "母鸡有" + j + "只" + "小鸡有" + k + "只");
                     }
            }
        }

    }
}

3.3 break和continue语句

1、break语句的用法
在switch语句中,使用break跳出switch语句,将控制留转到紧跟在switch之后的语句
在循环语句中,使用break跳出一个循环体
注意:
在嵌套循环语句中,break只是跳出其所在的最内层循环体。如果需要直接跳出多层循环,可以使用带标签的break语句。
2、continue语句的用法
continue语句不跳出所在循环体,而只是中断执行当前循环体的剩余部分,并进入下一轮循环。
例子:

package work;
public class test01 {
	public static void main(String []args) {
		int stop=4;
		for(int i=1;i<10;i++) {
			if(i==stop)break;
			 System.out.println("i="+i);//i=1,i=2,i=3
		}
	}

}

package work;
public class test01 {
	public static void main(String []args) {
		int stop=4;
		for(int i=1;i<10;i++) {
			if(i==stop)continue;
			 System.out.println("i="+i);//i=1,i=2,i=3,i=5,i=6,i=7,i=8,i=9,i=10
		}
	}

}

在这里插入图片描述

import java.util.Scanner;                     //导入Scanner类所在的包
public class Demo3_19 {
	public static void main(String[] args) {
int price = (int)(Math.random()*901)+100;
		System.out.println("商品价格在100-1000元之间,请输入你竞猜价格:");
		Scanner sc = new Scanner(System.in);
		System.out.println("生成的商品价格为"+price);
		double guess = sc.nextInt();
		int time = 1;
		while(guess!=price){
			if(time>5)
				break;
			if(guess>price){
				System.out.println("你给的价格太高了");
			}
			else{
				System.out.println("你给的价格太低了");					
			}
			guess = sc.nextInt();
			time++;
		}
		if(time<=5){
			System.out.println("你竞猜的价格正确,为:"+price+" 竞猜次数为:"+time);
		}
		else{
			System.out.println("你已经连续5次竞猜的价格不正确,商品价格为:"+price);
		}
	}
}

3.4 循环结构之String和Random

1、系统使用String类的trim()方法,去除用户输入字符串两端的空白,使用String类的equalslqnoreCase()方法,比较两个字符串内容是否相等,忽略大小写。

import java.util.Scanner;

public class test33 {
    public static void main(String args[]) {
    //字符串判定相符
        Scanner scanner=new Scanner(System.in);
        String b=scanner.next();
        if(b.equals("Y")){
            System.out.println("你输入Y了");
        }
    }
}

运算结果:
Y
你输入Y了
2、系统使用Random的nextInt()方法生成100以内的随机数。

import java.util.Random;

public class test33 {
    public static void main(String args[]) {
        //Random的使用
        Random abc= new Random();
        int a=abc.nextInt(100);
        System.out.println(a);//87
    }
}

3、使用for循环产生10道题目

 for(int i = 1; i<=numOfQuestion; i++){}

题目:
1、为小学生设计一共加减法测试系统,用户可选择是否开始退出,如果选择开始,可以选择题数,然后依次答题并显示是否正确,最后统计正确率以及成绩。

import java.util.Random;
import java.util.Scanner;
/*
为小学生设计一共加减法测试系统,
用户可选择是否开始退出,如果选择开始,可以选择题数,然后依次答题并显示是否正确,最后统计正确率以及成绩。
 */
public class test35 {
    public static void main(String[] args) {
        Scanner number = new Scanner(System.in);
        Random ran = new Random();
        System.out.println("您好,欢迎您参加本次测试!如果你选择继续测试,请输入1,如果不选择测试,请输入0退出");
        while (true) {
            System.out.println("请输入你的数字:");
            int exit = number.nextInt();
            if (exit == 0) {
                break;
            }
            System.out.println("请你输入测试题目的数量:");
            int quantity = number.nextInt();
            int right = 0;
            for (int i = 1; i <= quantity; i++) {
                int a = ran.nextInt(20);
                int b = ran.nextInt(20);
                System.out.println("请选择要进行哪种运算如:输入+,-");
                String symbol = number.next();//operation:运算
                System.out.println(a + symbol + b + "=");
                int yesright = number.nextInt();
                switch (symbol) {
                    case "+":
                        if (yesright == a + b) {
                            right++;
                            System.out.println("计算正确,点赞");
                        } else {
                            System.out.println("计算错误,继续加油");
                        }
                        break;
                    case "-":
                        if (yesright == a - b) {
                            right++;
                            System.out.println("计算正确,点赞");
                        } else {
                            System.out.println("计算错误,继续加油");
                        }
                        break;
                    default:
                        break;
                }
            }
                float result = (((float) right) / quantity) * 100;
                System.out.println("正确率是" + result + "%");
                System.out.println("成绩为" + result + "分");


            }
        }
    }

2、请完成以下练习:
显示:请输入操作类型:1:输入信息;2:显示信息;3:退出
如果输入1,依次:
显示:请输入人员信息:学号
显示:请输入人员信息:姓名
显示:请输入人员信息:爱好
显示:请输入人员信息:出生年份
如果输入2:
显示:请输入操作类型:1:学号;2:姓名;3:爱好;4:年份;5:返回上一级操作
输入1——显示学号;
输入2——显示姓名;
输入3——显示爱好;
输入4——显示出生年份以及此年度是否闰年
输入5——显示:请输入操作类型:1:输入信息;2:显示信息;3:退出
如果输入3:
显示:已退出系统

/*
    显示:请输入操作类型:1:输入信息;2:显示信息;3:退出
           如果输入1,依次:
                        显示:请输入人员信息:学号
                        显示:请输入人员信息:姓名
                        显示:请输入人员信息:爱好
                        显示:请输入人员信息:出生年份
           如果输入2:
                       显示:请输入操作类型:1:学号;2:姓名;3:爱好;4:年份;5:返回上一级操作
                            输入1——显示学号;
                            输入2——显示姓名;
                            输入3——显示爱好;
                            输入4——显示出生年份以及此年度是否闰年
                            输入5——显示:请输入操作类型:1:输入信息;2:显示信息;3:退出
           如果输入3:显示:已退出系统
     */
    import java.util.Scanner;
    public class test34 {
    public static void main(String[] args) {
        Scanner src = new Scanner(System.in);
        String ID = null;
        String name = null;
        String like = null;
        int birth=0;
        while (true) {
            System.out.println("显示:请输入操作类型:1:输入信息;2:显示信息;3:退出");
            int number = src.nextInt();

            if (number == 1) {
                System.out.println("请输入人员信息:学号");
                ID = src.next();
                System.out.println("请输入人员信息:姓名");
                name = src.next();
                System.out.println("请输入人员信息:爱好");
                like = src.next();
                System.out.println("请输入人员信息:出生年月信息");
                birth = src.nextInt();
            }
            if (number == 2) {
                while (true) {
                    System.out.println("请输入操作类型:1学号,2姓名,3爱好,4年份,5返回上一级操作");
                    int Result = src.nextInt();
                    if (Result == 1) {
                        System.out.println("学号是" + ID);
                    } else if (Result == 2) {
                        System.out.println("姓名是" + name);
                    } else if (Result == 3) {
                        System.out.println("爱好" + like);
                    } else if (Result == 4) {
                        System.out.println("年份" +birth);
                        if(birth%4==0 && birth%100!=0 || birth%400==0) {
                            System.out.println(birth+"年为闰年");

                        }else{
                            System.out.println(birth+"年不是闰年");
                        }

                    } else if (Result == 5) {
                        System.out.println("返回上一级操作");
                        break;
                    } else {
                        System.out.println("您输入的信息有误");
                    }
                }
            }
            if (number == 3) {
                break;

            }
        }
    }
}

四、面向对象编程

类(class)、对象(object)引用(reference)
在这里插入图片描述

问题的提出:
1、什么是面向对象?有哪些相关概念?
答:
面向对象是把整个需求按照特点、功能划分,将这些存在共性的部分封装成类(类实例化后才是对象),创建了对象不是为了完成某一个步骤,而是描述某个事物在解决问题的步骤中的行为。

面向对象的基本概念:
面向对象编程(OOP)的本质
以类的方式组织代码,以对象的方式组织数据
面向对象的思维:OOA
对象:具体的事物
类:对对象的抽象(抽象 抽出象的部分)
2、面向对象和面向过程有什么区别?
答:
面向对象——行为化(概念相对抽象)
面向过程——步骤化
面向过程就是分析出实现需求所需要的步骤,通过函数(方法)一步一步实现这些步骤,接着依次调用即可

面向过程:
优点:性能上它是优于面向对象的,因为类在调用的时候需要实例化,开销过大。
缺点:不易维护、复用、扩展

面向对象:
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护

缺点:一般来说性能比面向过程低
3、什么是对象?
答:对象是该类事物的具体表现形式,具体存在的个体。
4、什么是类?
答:一组相关的属性和行为的集合,是一个抽象的概念。
5、如何进行面向对象编程?
步骤:
1、抽象出类对象
2、抽象出类对象的属性
3、抽象出类对象的行为(方法)
4、根据类对象创建实例对象
5、通过实例对象访问属性和方法

1. 面向过程与面向对象的区别

例如:如何开汽车(比较简单,使用线性思维解决
面向过程:
1、踩离合
2、挂挡
3、踩油门,放离合
4、开了
面向对象:
1、驾驶员
2、汽车
驾驶员开汽车
在这里插入图片描述

2. 面向对象思想的特点:

a、是一种更符合我们思想的特点
b、可以讲复杂的事情简单化
c、将我们从执行者变成了指挥者
d、角色产生了转换

3. Java与面向对象

a、对象是java的核心
b、对象可以看成是静态属性和动态方法的封装体
c、类是用来创建同一类型的对象的“模板”,在一个类中应该定义这类对象具有的成员变量(属性)和方法(功能)
d、Java jdk中提供了大量的系统类,我们也可以定义自己的类。

五、类

1. 什么是类

a、类是一个概念(名词)抽象的定义。简单说就是分类。
b、类定义了该类型对象的数据结构,称之为
"成员变量"
,同时,也定义了一些可以被调用的功能,称之为**“方法”**
c、类是用于构建对象的模板,对象的实质就是内存中块存储区域,其数据结构由定义它的类来决定。
现实世界的事物:
1、属性:人的身高,体重等
2、行为:人可以学习,吃饭等
Java中用class描述事物也是如此
1、成员变量 就是事物的属性
2、成员方法 就是事物的行为
定义类其实就是定义类的成员(成员变量和成员方法)

//Point类定义了所有的Point对象都应该具有两个int类型的数据结构。
class Point{

int x;
int y;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. Java类的创建

class 类名
{
//成员变量
//方法
}
class:定义类的关键字
成员变量:【修饰符】数据类型 变量名;
方法:【修饰符】返回类型 方法名(方法的参数)
{
方法体;
}
eg:

class Point
{
private int x,y;
public void setPoint(int a,int b)
{
     x=a;
     y=b;
}
public int getX(){return x;}
public int getY(){return y;}
public String toString()
{
return"["+x+","+y+"]";}
}

六、对象

1 .对象的创建与使用

1、对象引用声明
——类名 对象引用名
2、创建对象
——对象引用名=new类名();
3、对象的使用
——对象引用名.成员变量(方法)
4、对象的内存分配
——同一类中的所有对象有不同的成员变量内存空间
——同一类中的所有对象共享方法的内存空间

5、 Point thePoint
注意:类属于复合数据类型,因此,在声明对象时,系统并没有为对象分配空间,用户需要应用new完成分配空间的任务。
thePoiint=new Point();

JAVA实例——对象使用

class Circle1
{
float r;
final double PI=3.14159;
public double area() //计算面积
{
     return PI*r*r;
}
public void setR(float x)//设置半径
{
       r=x;
}
public double perimeter() //计算周长
{
      return 2*PI*r;
}
public static void main(String args[])
{
    double x,y;
    Circle1 cir=new Circle1(); //创建Circle1类的对象cir
    cir.setR(12.35f); //引用cir对象的setR()方法
    x=cir.area();  //引用cir对象的area()方法
    y=cir.perimeter(); //引用cir对象的perimeter()方法
    System.out.println("圆的面积="+x+"\n圆的周长="+y);
    }
  }

课堂活动:
实现对应类的两个对象,并调用方法
1.在刚才的类图基础上完成此任务;
2.定义学生类,要求属性:学号、姓名、性别、宿舍号、籍贯
方法:自我介绍
生成两个对象,一个是自己的信息,另一个是自己的舍友或者团队的队友;
设置属性,调用方法
答:代码如下:
代码1:

public class Student {
    String name,stuID,sex,bID,birPlace;

    //构造方法
    Student() {
        name = "张三";
        stuID = "000001";
        sex = "男";
        bID = "001";
        birPlace = "安徽省";
    }
    Student(String n,String s){
        name=n;
        stuID="000002";
        sex=s;
        bID="002";
        birPlace="安徽省";
        }
    Student(String name){
        this.name=name;
        stuID="000003";
        sex="男";
        bID="003";
        birPlace="安徽省";
    }
    //方法
    public void intro(){
        System.out.println("大家好,很高兴认识大家,我的名字是"+name+",性别为"+sex+",我来自"+birPlace+",我的学号为"+stuID);
    }
}

代码2:

public class test38 {
    public static void main(String[]args){
       Student a,b,c;
       a=new Student();
       b=new Student("李四","女");
       c=new Student("王二");
       a.intro();
       b.intro();
       c.intro();

    }

}

2. 构造方法

2.1 构造方法的作用

——对象初始化(创建对象的同时给对象赋值)

2.2 构造方法定义的格式——方法类似

[修饰符] 类名(方法的形参){
方法体;
}

2.3 构造方法调用——在创建对象的同时进行调用

类名 对象引用名;
对象引用名=new 类名(实参);

2.4 Java实例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

完成下列实例:

1、定义宠物类;

2、使用对象来完成以下功能:

运行程序后,由用户输入宠物名字和类别【用户可以选择输入名字(此时宠物为猫),也可以同时输入名字和类别】。

在输入完毕后,生成对象(使用构造方法),并通过对象的方法显示宠物信息。

public class petee {
    //成员变量
    String name;   //宠物的名字
    String color; //毛发颜色
    int age;      //年龄
    double weight;//体重
    double price;//售价
    String category;//类别

    //构造方法
    petee(String category,String name){
        this.name=name;
        color="blue";
        age=5;
        weight=10;
        price=20;
        this.category = category;
    }
    petee(String n){
        name=n;
        color="black";
        age=6;
        weight=12;
        price=30;
        category = "猫";

    }

    //方法

    public void intro()//显示属性
    {
        System.out.println("宠物的类别为"+category);
        System.out.println("宠物的名字为"+name);
        System.out.println("宠物的毛发颜色为"+color);
        System.out.println("宠物的年龄为"+age);
        System.out.println("宠物的体重为"+weight);
        System.out.println("宠物的售价为"+price);


    }

}
import java.util.Scanner;

public class petn {
    public static void main(String[] args) {
        Scanner src = new Scanner(System.in);
        System.out.println("欢迎您来到宠物店!");
        // 1、 输入宠物名称
        System.out.print("请输入要领养猫的名字:");
        String name1 = src.next();
        petee p1 =new petee(name1);
        p1.intro();


        System.out.println("========");
        System.out.print("请输入宠物的类型:");
        String temp = src.next();
        System.out.print("请输入"+temp+"宠物的名字:");
        String name2 = src.next();
        petee p2 = new petee(temp,name2);
        p2.intro();


    }

}

在这里插入图片描述

2.5 构造方法回顾:

1,构造方法作用:(1)构造出来一个类的实例 (2)对构造出来个一个类的实例(对象)初始化
2,构造方法的名字必须与定义他的类名完全相同,没有返回类型,甚至连void也没有
3,主要完成对象的初始化工作,构造方法的调用是在创建一个对象时使用new操作进行的
4,类中必定有构造方法,若不写,系统自动添加无参构造方法。接口不允许被实例化,所以接口中没有构造方法
5,不能被static、final、synchronized、abstract和native修饰
6,构造方法在初始化对象时自动执行,一般不能显式地直接调用.
当同一个类存在多个构造方法时,java编译系统会自动按照初始化时最后面括号的参数个数以及参数类型来自动一一对应
完成构造函数的调用]
7,构造方法可以被重载。没有参数的构造方法称为默认构造方法,与一般的方法一样,构造方法可以进行任何活动
但是经常将他设计为进行各种初始化活动,比如初始化对象的属性
8,构造代码块:
    (1)作用:给对象进行初始化,对象一建立就执行,而且优先于构造函数执行
  (2)构造代码块和构造函数的区别:
   构造代码块是给所有不同对象的共性进行统一初始化,构造函数是给对应的对象进行初始化
9,自定义类中,如果不写构造方法,java系统会默认添加一个无参的构造方法。
如果写了一个有参的构造方法,就一定要写无参构造方法。一般情况下,我们自定义的类都要手动给出无参构造方法。

七、封装

1.面向对象三大特征(封装)

1、将类中所有的功能通过方法实现。
2、隐藏对象的成员变量,为什么?
答:权限设置,数据验证
3、如何隐藏?
答:权限修饰符:public,protected,private,默认

2.类的封装

封装性是面向对象的核心特征之一。
1、 类的封装包含2层含义:
(1)将数据和对数据的操作组合起来构成类,类是一个不可分割的独立单位。
(2)类中既要提供与外部联系的接口,同时又要尽可能隐藏类的实现细节。
2、优点
(1)良好的封装能够减少耦合
(2)类内部的结构可以自由修改
(3)可以对成员变量进行更精确的控制
(4)隐藏信息,实现细节。

3.访问权限

1、成员访问权限
(1)public(公有):被public修饰的成员变量和成员方法可以在所有类中访问。
(2)protected(保护):被protected修饰的成员变量和成员方法可以在声明他们的类中访问,在该类的子类中访问,也可以在与该类位于同一包的类中访问,但不能位于其它包的非子类中访问。
(3)缺省:缺省指不使用权限修饰符。不使用权限修饰符修饰的成员变量和方法可以在声明他们的类中访问,也可以在与该类位于同一包的类中访问,但不能位于其它包的非子类中访问。
(4)private(私有):被private修饰的成员变量和成员方法只能在声明他们的类中访问,而不能在其他类(包括其子类)中访问。
在这里插入图片描述
2、举例:
例1:

public class person {
    private int age;
    private String ID;
    private String name;
    public String room;
        {
            name = "张三";
            age = 18;
            ID = "123456";
        }
 public String getID(){
            return ID;
        }
    public String getName(){
        return name;
    }
    public int getAge(){
            return age;
    }
public String getRoom(){
            return room;
}
    }


public class peson1 {
    public static void main(String[]args){
        person children=new person();
        children.room="sss号";
        
        System.out.println(children.getID());
        System.out.println(children.getName());
        System.out.println(children.getAge());
        System.out.println(children.room);
    }
}

例2:

public class number {
    int a;
    {
        a=12;
        System.out.println("构造代码块");
    }
    public number(){
        a=3;
        System.out.println("无参构造方法");
    }
    public number(int b){
        a=b;
        System.out.println("有参数构造方法");
    }
    public static void main(String[]args){
        number abc=new number();
        number bcd=new number(11);
        System.out.println(abc.a);
        System.out.println(bcd.a);
    }
}
//运行结果为
//构造代码块
//无参构造方法
//构造代码块
//有参数构造方法
//3
//11


3、类访问权限
声明一个类可使用的权限修饰符只有public和缺省2种。虽然一共java源程序文件中可以包含多个类,但只能有一个使用public修饰符,该类的名字与源程序文件的名字相同。当程序中创建多个类时,必须运行包含main()方法的类,否则出错。
注意:
1、构造代码块优先于构造函数执行
2、构造代码块:
作用:给对象进行初始化。
对象一建立就运行,而且优先于构造函数执行。
3、构造代码块与构造函数的区别:
构造代码块是给所有对象进行统一初始化,
而构造函数是给对应的对象初始化。
构造代码快中定义的是不同对象共性的初始化内容。

4.静态成员与静态方法

静态成员-static
static-静态成员(类成员)
static修饰的变量:公共变量。一个类中的所有对象共用静态变量的内存空间。
static修饰的方法:静态方法。在调用该方法时,不会传递对象的引用值。
结论:
静态成员在第一次使用类时分配内存空间。
静态成员可通过类名直接调用,也可以通过对象引用。
静态方法不能直接访问非静态成员。
类成员
类成员变量
在类中声明成员变量,没有使用static修饰的变量为实例成员变量,使用static修饰的变量为类成员变量。

class Student
{
String name; //实例成员变量
String sex; //实例成员变量
static int count=0; //类成员变量
public Student(String m,String s)
{
name=m;
sex=s;
count=count+1;}}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述

4.1.方法参数的传递

原来值(输出)-》作为参数运行方法(输出)-》新的值(输出)
通过基本类型、引用类型(数组、对象)进行对比

public static void main(String []args) {
        int x;
		x=10;
       System.out.println("Hello World!"+x);
       }
       static public void test(int x){
       x=x+10;
		System.out.println("Hello World!"+x);
	}
 public static void main(String []args) {
		int x[]={1,2};
		
       System.out.println("Hello World!"+x[0]);
		test(x);
		System.out.println("Hello World!"+x[0]);
    }
	
	
	
	static public void test(int x[]){
		
		x[0]=x[0]+10;
		System.out.println("Hello World!"+x[0]);
	}
public class HelloWorld {
	
    public static void main(String []args) {
		X x=new X();
		
		
       System.out.println("Hello World!"+x.x);
		test(x);
		System.out.println("Hello World!"+x.x);
    }
	
	
	
	static public void test(X x){
		
		x.x=x.x+10;
		System.out.println("Hello World!"+x.x);
	}
	
}


class X{
int x;
	{x=10;}
}
package work;

public class test02 {
	
	public static void main(String[] args) {
		int x[]= {10,2};
		
		System.out.println(x[0]);
		
		show(x);
		
		System.out.println(x[0]);
		
}
	static void show(int x[]) {
		x[0]=x[0]+10;
		System.out.println(x[0]);
	}
}

4.2.方法的重载

在这里插入图片描述
在这里插入图片描述

package work;

public class test {
	public static void main(String []args) {
	test ss=new test();
	System.out.println(ss.max(10,4));
	System.out.println(ss.max(100,4,3));
	System.out.println(ss.max(300,400,3,6));
}
   private int max(int a,int b) {
	   if(a<b)
	   {
		   a=b;
	   }
		   return a;
	   }
	   private int max(int a,int b,int c) {
		  a=max(a,b);
		  a=max(a,c);
		  
		  return a;
	   }
	   private int max(int a,int b,int c,int d) {
			  a=max(a,b);
			  a=max(a,c);
			  a=max(a,d);
			  
			  return a;
		   }
}

课堂练习
完成例5.4前两项,最后完成完整的题目作为作业。

public class Example5_03
{
	public static void main(String args[])
	{
		int a=15,b=25;
		System.out.println(a+"+"+b+"="+add(a,b));//调用第1个add
		
		double x=1234.567,y=987.665;
		System.out.println(x+"+"+y+"="+add(x,y));//调用第3个add
		
		long m=1234566788L,n=9876543210L;//常数后加L表示长整型数
		System.out.println(m+"+"+n+"="+add(m,n));//调用第2个add
		
	}
	//计算两个int型数据之和
	static int add(int a,int b)
	{
		return a+b;
	}
	//计算两个long型数据之和
	static long add(long a,long b)
	{
		return a+b;
	}
	//计算两个double型数据之和
	static double add(double a,double b)
	{
		return a+b;
	}	
}

在这里插入图片描述
在这里插入图片描述

class Point2//点类
{
	private int x,y;//域,点的位置
	
	public Point2(int x,int y)//构造方法,用一对坐标值构造
	{
		this.x=x;
		this.y=y;
	}
	public Point2(Point2 p)//构造方法,用一个本身类对象构造
	{
		x=p.x;
		y=p.y;
	}
	public int getX()//获取x分量
	{
		return x;
	}
	public int getY()//获取y分量
	{
		return y;
	}
	//移动点的方法,x和y向的增量分别是offsetX和offsetY
	public void move(int offsetX,int offsetY)
	{
		x=x+offsetX;
		y=y+offsetY;
	}
}
class Circle5//组合类
{
	private double radius;
	private Point2 center;//Point类的对象
	
	//构造方法,用点坐标(x,y)构造圆对象
	public Circle5(double radius,int x,int y)
	{
		this.radius=radius;
		center=new Point2(x,y);
	}
	//构造方法,用点类的对象构造圆对象
	public Circle5(double radius,Point2 p)
	{
		this.radius=radius;
		center=new Point2(p);
	}
	public double area()//计算圆面积
	{
		return Math.PI*radius*radius;
	}
	public double perimeter()//计算圆周长
	{
		return 2*Math.PI*radius;
	}
	public int getCenterX()//获取圆心x坐标
	{
		return center.getX();//直接调用点类中的方法获取,重用Point中的代码
	}
	public int getCenterY()//获取圆心y坐标
	{
		return center.getY();//直接调用点类中的方法获取,重用Point中的代码
	}
	public void move(int offsetX,int offsetY)//移动圆
	{
		center.move(offsetX, offsetY);//直接调用点类中的方法移动,重用Point中的代码
	}	
}
public class Example5_12
{
	public static void main(String args[])
	{
		int x=15,y=25;
		double radius=10;
		
		Circle51=new Circle5(radius,x,y);//用第1个构造方法创建圆对象
		System.out.printf("圆1的面积:%.2f,",1.area());
		System.out.printf("圆1的周长:%.2f\n",1.perimeter());
		
		System.out.print("移动前圆1的位置:");
		System.out.println("("+1.getCenterX()+","+1.getCenterY()+")");1.move(11, 12);//移动圆
		System.out.print("移动后圆1的位置:");
		System.out.println("("+1.getCenterX()+","+1.getCenterY()+")");
		
		Point2 p=new Point2(-15,-25);//创建1个点类的对象
		radius=15;
		
		Circle52=new Circle5(radius,p);//用第2个方法创建圆对象
		System.out.printf("圆2的面积:%.2f,",2.area());
		System.out.printf("圆2的周长:%.2f\n",2.perimeter());
		
		System.out.print("移动前圆2的位置:");
		System.out.println("("+2.getCenterX()+","+2.getCenterY()+")");2.move(-11,-12);//移动圆
		System.out.print("移动后圆2的位置:");
		System.out.println("("+2.getCenterX()+","+2.getCenterY()+")");
	}
}

八、包

1.包的概念

在这里插入图片描述

2.包的作用

  1、区分相同名称的类。
  2、能够较好地管理大量的类。
  3、控制访问范围。

Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。
包语句的语法格式为:

package pkg1[.pkg2[.pkg3…]];

例如:一个Something.java 文件它的内容

package net.java.util;
public class Something{
   ...
}

那么它的路径应该是 net/java/util/Something.java 这样保存的。 package(包) 的作用是把不同的 java 程序分类保存,更方便的被其他 java 程序调用。

3.包的概念说明

在这里插入图片描述

4.创建自定义包

在这里插入图片描述
在这里插入图片描述
1、例子
让我们来看一个例子,这个例子创建了一个叫做animals的包。通常使用小写的字母来命名避免与类、接口名字的冲突。

a、在 animals 包中加入一个接口(interface):
Animal.java 文件代码:

/* 文件名: Animal.java */
package animals;
 
interface Animal {
   public void eat();
   public void travel();
}

b、接下来,在同一个包中加入该接口的实现:
MammalInt.java 文件代码:

package animals;
 
/* 文件名 : MammalInt.java */
public class MammalInt implements Animal{
 
   public void eat(){
      System.out.println("Mammal eats");
   }
 
   public void travel(){
      System.out.println("Mammal travels");
   } 
 
   public int noOfLegs(){
      return 0;
   }
 
   public static void main(String args[]){
      MammalInt m = new MammalInt();
      m.eat();
      m.travel();
   }
}

结果为:
Mammal eats
Mammal travel

5.引用包策略

在这里插入图片描述
1、 使用 import 语句导入指定包下全部类的用法按如下:

import example.*;

上面 import 语句中的星号(*)只能代表类,不能代表包,表明导入 example 包下的所有类。

提示:使用星号(*)可能会增加编译时间,特别是引入多个大包时,所以明确的导入你想要用到的类是一个好方法,需要注意的是使用星号对运行时间和类的大小没有影响。

2、通过使用 import 语句可以简化编程,但 import 语句并不是必需的,如果在类里使用其它类的全名,可以不使用 import 语句。

3、Java 默认为所有源文件导入 java.lang 包下的所有类,因此前面在 Java 程序中使用 String、System 类时都无须使用 import 语句来导入这些类。但对于前面介绍数组时提到的 Arrays 类,其位于 java.util 包下,则必须使用 import 语句来导入该类。

4、在一些极端的情况下,import 语句也帮不了我们,此时只能在源文件中使用类全名。例如,需要在程序中使用 java.sql 包下的类,也需要使用 java.util 包下的类,则可以使用如下两行 import 语句:

import java.util.*;
import java.sql.*;

如果接下来在程序中需要使用 Date 类,则会引起如下编译错误:
Test.java:25:对Date的引用不明确,
java.sql中的类java.sql.Date和java.util中的类java.util.Date都匹配
上面错误提示:在 Test.java 文件的第 25 行使用了 Date 类,而 import 语句导入的 java.sql 和 java.util 包下都包含了 Date 类,系统不知道使用哪个包下的 Date 类。在这种情况下,如果需要指定包下的 Date 类,则只能使用该类的全名,代码如下:

// 为了让引用更加明确,即使使用了 import 语句,也还是需要使用类的全名
java.sql.Date d = new java.sql.Date();

课堂活动:思考下列场景,设计包结构
我们上课需要很多章节,每个章节有很多个类,其中有一些类名会有重复
例如:
在这里插入图片描述

Test1.java

package example;
import ex.ch1JavaFirst.Test;
//import ex.ch1JavaFirst.*;

public class Test1 {
    public static void main(String[]args){
        {
        //ex.ch1JavaFirst.Test test =new ex.ch1JavaFirst.Test();和 Test test=new Test();结果一样,不用import引用
            Test test=new Test();
            test.intro();
        }
    }

}

ch1JavaFirst.java

package ex.ch1JavaFirst;

public class Test {

    public void intro()
    {
        System.out.println("SSSS");
    }

}

6. Java类库

6.1. Java类库

在这里插入图片描述 在这里插入图片描述
在这里插入图片描述

6.2. 数学函数类——Math类库

在这里插入图片描述
在这里插入图片描述
三、课堂练习:
1、包的使用
a、创建一个名为 com.dao 的包。
b、添加一个 Student 类,该类包含一个返回 String 类型数组的 GetAll() 方法。Student 类代码课参考

package com.dao;

public class Student {
    public static String[]GetAll(){
        String[]namelist={"李潘","邓国良","任玲玲","许月月"};
        return namelist;
    }
}

c、创建 com.test 包,在该包里创建带 main() 方法的 Test 类。
d、在 main() 方法中遍历 Student 类的 GetAll() 方法中的元素内容,在遍历内容之前,使用 import 引入 com.dao 整个包。

package com.test;

import com.dao.Student;

public class Test {
    public static void main(String []args){
        System.out.println("学生的信息如下:");
        for(String str: Student.GetAll()){
            System.out.println(str);
        }
    }
}

6.3. 继承

在这里插入图片描述
为什么需要继承
1、使用继承可以有效实现代码的复用,避免重复代码的出现
2、当两个类具有相同的特征(属性)和行为(方法)时,可以相同的部分抽取出来放到一个类中作为父类,其他两个类继承这个父类。
3、为了扩展原本类的功能加强或改进原本类所没有定义的属性及方法
4、建立类与类之间的层级关系
在这里插入图片描述
在这里插入图片描述
继承语法格式:
示例:
Animal.java

package workpace;

public class Animal {
	String name;
	
	{
		name="动物名";
		
	}
	
	void shout() {
		System.out.println("传来了动物的叫声");
	}
	void showAnimalName() {
		System.out.println("小猫");
	}

}

Cat.java

package workpace;
//
//public class Cat {
//	String name;
//	
//	void shout() {
//		System.out.println("传来了猫的叫声");
//	}
//
//}


public class Cat extends Animal{

	{
		name="小猫";//父类定义name,子类继承可以用
		
	}
	
	void shout() {
		System.out.println("传来了喵喵的叫声");
	}
	void eat() {
		System.out.println("这条鱼真好吃");
		
	}
	void showCatName() {
		System.out.println(name);
	}
}

Test.java

package workpace;

public class Test {
	public static void main(String []args) {
		 Cat cat=new Cat();
		 cat.shout();
		 cat.eat();
		 //System.out.println(cat.name);
		 cat.showAnimalName();
		 cat.showCatName();
		 
		 Animal ani =new Animal();
	}

}

6.3.1 继承知识点——方法的重写

在这里插入图片描述
在这里插入图片描述

class A//父类
{
	int var;//整型域,无访问属性,可被子类直接访问
	
	public void setVarA(int v)//设置域值
	{
		var=v;
	}
	public int getVarA()//获得域值
	{
		return var;
	}
}
class B extends A//子类,继承自A
{
	//长整型域,私有,与继承自父类的域同名,
	//本类中隐藏了继承自父类的同名域
	private long var;
	
	public void setVarB(long v)//设置域值
	{
		var=v;//访问的var是本身类定义的,不是继承自父类
	}
	public long getVarB()//获得域值
	{
		return var;//访问的var是本身类定义的,不是继承自父类
	}
}
public class Example6_03
{
	public static void main(String args[])
	{
		B b=new B();//创建对象b,b中有两个同名域var
		
		b.setVarA(1234567890);//调用继承自父类的方法设置继承自父类的var
		b.setVarB(12345678900L);//调用本身的方法设置本身类定义的var
		
		System.out.println(b.getVarA());//输出继承自父类的var
		System.out.println(b.getVarB());//输出本身类定义的var
	}
}

在这里插入图片描述
父类的方法会被子类继承,无论访问修饰符!

父类的private方法会被继承,但无法访问!
这两点与上面属性的效果一致,所以不再累述。

子类可以拥有和父类同名方法,符合重写规范的话会发生覆盖。
方法重写的规范包括:

1、方法名相同;
2、参数列表相同(包括参数个数、类型、顺序三者都相同);
3、返回类型相同;
4、子类方法的访问修饰符不能小于父类方法的访问修饰符;
5、子类方法的方法声明不能比父类方法的方法声明抛出更多的异常。
只有这5点均满足的情况下,才会发生重写;否则会报错或两个方法共存(重载)

class Employee//雇员类,父类
{
	protected String name;//姓名
	protected double 工资;
	
	public Employee(){}//构造方法
	public Employee(String name,double 工资)//构造方法
	{
		this.name=name;
		this.工资=工资;
	}
	public double 领工资()//给雇员发工资
	{
		return 工资;
	}
	public String getName()
	{
		return name;
	}
}
class Manager extends Employee//定义经理子类,Employee是父类
{
	private double 津贴;
	
	public Manager(String name,double 工资,double 津贴)//构造方法
	{
		this.name=name;
		this.工资=工资;
		this.津贴=津贴;
	}
	public double 领工资()//对继承自父类的方法重写,因为工资计算方法不同
	{
		return 工资+津贴;
	}
}
class Technician extends Employee//定义技术人员子类,Employee是父类
{
	private int 工作小时数;
	private double 单位津贴;
	
	public Technician(String name,double 工资,int 工作小时数,double 单位津贴)
	{
		this.name=name;
		this.工资=工资;
		this.工作小时数=工作小时数;
		this.单位津贴=单位津贴;
	}
	public double 领工资()//对继承自父类的方法重写,因为工资计算方法不同
	{
		return 工资+工作小时数*单位津贴;
	}
}

public class Example6_04
{
	public static void main(String args[])
	{
		//创建三个雇员对象
		Employee 秘书=new Employee("张秘书",2800.00);
		Manager 经理=new Manager("王经理",2800.00,2600.00);
		Technician 工程师=new Technician("刘工",2800.00,160,15.00);
		
		//雇员领工资
		System.out.printf("%s工资:%.2f\n",秘书.getName(),秘书.领工资());
		System.out.printf("%s工资:%.2f\n",经理.getName(),经理.领工资());
		System.out.printf("%s工资:%.2f\n",工程师.getName(),工程师.领工资());
	}
}
// 定义Animal类
class Animal {     
    //定义动物叫的方法    
    voidshout() {           
        System.out.println("动物发出叫声");
    }
}
// 定义Dog类继承动物类
class Dog extends Animal {   
    // 定义狗叫的方法
    void shout() {           
        System.out.println("汪汪……");
    }
}
6.3.2. this 的使用

一、调用成员方法和属性

package com.wz.extendsdemo;

class Person {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }
}

class Student extends Person { // Student类继承了Person类
}

public class TestDemo {
    public static void main(String args[]) {
        Student stu = new Student(); // 实例化的是子类
        stu.setName("张三"); // Person类定义
        stu.setAge(20); // Person类定义
        System.out.println("姓名:" + stu.getName() + ",年龄:" + stu.getAge());
    }
}

运行结果:姓名:张三,年龄:20
通过上述代码可以发现子类(Student)并没有定义任何的操作,而在主类中所使用的全部操作都是由Person类定义的,这证明:子类即使不扩充父类,也能维持父类的操作

package com.wz.extendsdemo;

class Person {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }
}

class Student extends Person { // Student类继承了Person类
    private String school; // 子类的属性

    public void setSchool(String school) {
        this.school = school;
    }

    public String getSchool() {
        return this.school;
    }
}

public class TestDemo {
    public static void main(String args[]) {
        Student stu = new Student(); // 实例化的是子类
        stu.setName("张三"); // Person类定义
        stu.setAge(20); // Person类定义
        stu.setSchool("xx大学"); // Student类扩充方法
        System.out.println("姓名:" + stu.getName() + ",年龄:" + stu.getAge() + ",学校:" + stu.getSchool());
    }
}

运行结果:姓名:张三,年龄:20,学校:xx大学
通过上述的代码,子类对于父类的功能进行了扩充(扩充了一个属性和两个方法)。但是思考一下:子类从外表看是扩充了父类的功能,但是对于以上的代码,子类还有一个特点:子类实际上是将父类定义的更加的具体化的一种手段。父类表示的范围大,而子类表示的范围小。

二、构造方法
子类无显式定义构造方法或者定义的构造方法中无调用父类无参构造方法的相关代码,系统默认调用父类无参构造方法(如果父类无无参构造方法呢?怎么解决这个问题?

三、Super使用:调用父类属性

在这里插入图片描述
在这里插入图片描述
通过这个对比我们可以看到,凡是用super能够操作到的属性或行为,this一定能够操作;而用this能够操作到的,super却不一定能够操作到;所以尽可能在代码中使用this来进行调用。
那么,什么时候使用super.操作呢?就是在调用重写方法的时候,this.重写方法()得到的是重写后的效果(即子类中定义的效果);用super.重写方法()得到的是重写前的效果(即父类中定义的效果)。
Java 中 this 和 super 的用法总结

在这里插入图片描述

在这里插入图片描述

7. 面向对象三大特征——多态

在这里插入图片描述
在这里插入图片描述

7. 1.抽象类与接口

在这里插入图片描述

如何在现实生活中理解抽象类:
实现对子类的约束,设计理念:抽取子类共性。
在 Java 中抽象类的语法格式如下:

<abstract>class<class_name> {
    <abstract><type><method_name>(parameter-iist);
}

抽象方法的 3 个特征如下:
抽象方法没有方法体
抽象方法必须存在于抽象类中
子类重写父类时,必须重写父类所有的抽象方法

注意:在使用 abstract 关键字修饰抽象方法时不能使用 private 修饰,因为抽象方法必须被子类重写,而如果使用了 private 声明,则子类是无法重写的。
抽象类的定义和使用规则如下:
1、抽象类和抽象方法都要使用 abstract 关键字声明。
2、如果一个方法被声明为抽象的,那么这个类也必须声明为抽象的。而一个抽象类中,可以有 0-n个抽象方法,以及 0-n个具体方法。
3、抽象类不能实例化,也就是不能使用 new 关键字创建对象。

不同几何图形的面积计算公式是不同的,但是它们具有的特性是相同的,都具有长和宽这两个属性,也都具有面积计算的方法。那么可以定义一个抽象类,在该抽象类中含有两个属性(width 和 height)和一个抽象方法 area( ),具体步骤如下。
1、首先创建一个表示图形的抽象类 Shape,代码如下所示。

public abstract class Shape {    
public int width; // 几何图形的长    
public int height; // 几何图形的宽    
public Shape(int width, int height) {        
this.width = width;        
this.height = height;    
}    
public abstract double area(); // 定义抽象方法,计算面积
}

2、定义一个正方形类,该类继承自形状类 Shape,并重写了 area( ) 抽象方法。正方形类的代码如下:

public class Square extends Shape {
    public Square(int width, int height) {
        super(width, height);
    }

    // 重写父类中的抽象方法,实现计算正方形面积的功能
    @Override
    public double area() {
        return width * height;
    }
}

3、定义一个三角形类,该类与正方形类一样,需要继承形状类 Shape,并重写父类中的抽象方法 area()。三角形类的代码实现如下:

public class Triangle extends Shape {
    public Triangle(int width, int height) {
        super(width, height);
    }

    // 重写父类中的抽象方法,实现计算三角形面积的功能
    @Override
    public double area() {
        return 0.5 * width * height;
    }
}

4、最后创建一个测试类,分别创建正方形类和三角形类的对象,并调用各类中的 area() 方法,打印出不同形状的几何图形的面积。测试类的代码如下:

public class ShapeTest {
    public static void main(String[] args) {
        Square square = new Square(5, 4); // 创建正方形类对象
        System.out.println("正方形的面积为:" + square.area());
        Triangle triangle = new Triangle(2, 5); // 创建三角形类对象
        System.out.println("三角形的面积为:" + triangle.area());
    }
}

在该程序中,创建了 4 个类,分别为图形类 Shape、正方形类 Square、三角形类 Triangle 和测试类 ShapeTest。其中图形类 Shape 是一个抽象类,创建了两个属性,分别为图形的长度和宽度,并通过构造方法 Shape( ) 给这两个属性赋值。

在 Shape 类的最后定义了一个抽象方法 area( ),用来计算图形的面积。在这里,Shape 类只是定义了计算图形面积的方法,而对于如何计算并没有任何限制。也可以这样理解,抽象类 Shape 仅定义了子类的一般形式。

正方形类 Square 继承抽象类 Shape,并实现了抽象方法 area( )。三角形类 Triangle 的实现和正方形类相同,这里不再介绍。

在测试类 ShapeTest 的 main( ) 方法中,首先创建了正方形类和三角形类的实例化对象 square 和 triangle,然后分别调用 area( ) 方法实现了面积的计算功能。

课堂活动:
分析课本案例部分,按照抽象类的思路进行重新设计。

作业:完成宠物类的设计,要求使用抽象类设计宠物,并实现至少两个子类,每个子类至少具有一个自有方法。请将测试结果截图与源码一起提交。
举例:
Aniaml.java


public class Animal {
	String name;
	{
	name="一只动物";
	}
	public void eat() {
		System.out.println(name+"要吃东西");
	}

}

Cat.java


public class Cat extends Animal {

	public void eat() {
		super.eat();
		System.out.println("原来小猫爱吃鱼");
	}

	public void CatchMouse() {
		System.out.println("这只小猫爱抓老鼠");
	}
}

text.java


public class text {
	
public static void main(String[]args) {
	Animal animalCat= new Cat();
	animalCat.eat();
	
	((Cat)animalCat).CatchMouse();
}

}

九、接口

如何在生活中理解接口:
生活中的接口:提供一种机制,可以连接各种不同的物品,实现具体功能。如电风扇、洗衣机都可以通过一个接口实现用电,洗衣机、花洒都可以通过水龙头实现用水。
多继承的概念;多继承:洗衣机继承电器、水、电的相关特性。

如果想实现多继承的便利性,怎么实现?为什么不采用类似C++的多继承机制?

1.定义接口

[public] interface interface_name [extends interface1_name[, interface2_name,]] {    // 接口体,其中可以包含定义常量和声明方法     
[public] [static] [final] type constant_name = value;    // 定义常量    
[public] [abstract] returnType method_name(parameter_list);    // 声明方法
}

对以上语法的说明如下:
public 表示接口的修饰符,当没有修饰符时,则使用默认的修饰符,此时该接口的访问权限仅局限于所属的包;
interface_name 表示接口的名称。
接口名应与类名采用相同的命名规则,即如果仅从语法角度来看,接口名只要是合法的标识符即可。如果要遵守 Java 可读性规范,则接口名应由多个有意义的单词连缀而成,每个单词首字母大写,单词与单词之间无需任何分隔符。
extends 表示接口的继承关系;
interface1_name 表示要继承的接口名称;
constant_name 表示变量名称,一般是 static 和 final 型的;
returnType 表示方法的返回值类型;
parameter_list 表示参数列表,在接口中的方法是没有方法体的。
注意:一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类。

接口对于其声明、变量和方法都做了许多限制,这些限制作为接口的特征归纳如下:
具有 public 访问控制符的接口,允许任何类使用;没有指定 public 的接口,其访问将局限于所属的包。
方法的声明不需要其他修饰符,在接口中声明的方法,将隐式地声明为公有(public)和抽象的(abstract)。
在 Java 接口中声明的变量其实都是常量,接口中的变量声明,将隐式地声明为 public、static 和 final,即常量,所以接口中定义的变量必须初始化。
接口没有构造方法,不能被实例化。例如:

public interface A {
    publicA(){}    // 编译出错,接口不允许定义构造方法
}

一个接口不能够实现另一个接口,但它可以继承多个其他接口。子接口可以对父接口的方法和常量进行重写。例如:

public interface StudentInterface extends PeopleInterface {
    // 接口 StudentInterface 继承 PeopleInterface
    int age = 25;    // 常量age重写父接口中的age常量
    void getInfo();    // 方法getInfo()重写父接口中的getInfo()方法
}

例如,定义一个接口 MyInterface,并在该接口中声明常量和方法,如下:

public interface MyInterface {    // 接口myInterface
    String name;    // 不合法,变量name必须初始化
    int age = 20;    // 合法,等同于 public static final int age = 20;
    void getInfo();    // 方法声明,等同于 public abstract void getInfo();
}

2. 实现接口

接口的主要用途就是被实现类实现,一个类可以实现一个或多个接口,继承使用 extends 关键字,实现则使用 implements 关键字。因为一个类可以实现多个接口,这也是 Java 为单继承灵活性不足所作的补充。类实现接口的语法格式如下:

<public> class <class_name> [extends superclass_name] [implements interface1_name[, interface2_name…]] {
    // 主体
}

对以上语法的说明如下:
public:类的修饰符;
superclass_name:需要继承的父类名称;
interface1_name:要实现的接口名称。

实现接口需要注意以下几点:
实现接口与继承父类相似,一样可以获得所实现接口里定义的常量和方法。如果一个类需要实现多个接口,则多个接口之间以逗号分隔。
一个类可以继承一个父类,并同时实现多个接口,implements 部分必须放在 extends 部分之后。
一个类实现了一个或多个接口之后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是重写这些抽象方法);否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类。
例 1
在程序的开发中,需要完成两个数的求和运算和比较运算功能的类非常多。那么可以定义一个接口来将类似的功能组织在一起。下面创建一个示例,具体介绍接口的实现方式。
(1)创建一个名称为 IMath 的接口,代码如下:

public interface IMath {
    public int sum();    // 完成两个数的相加
    public int maxNum(int a,int b);    // 获取较大的数
}

(2) 定义一个 MathClass 类并实现 IMath 接口,MathClass 类实现代码如下:


public class MathClass implements IMath {
    private int num1;    // 第 1 个操作数
    private int num2;    // 第 2 个操作数
    public MathClass(int num1,int num2) {
        // 构造方法
        this.num1 = num1;
        this.num2 = num2;
    }
    // 实现接口中的求和方法
    public int sum() {
        return num1 + num2;
    }
    // 实现接口中的获取较大数的方法
    public int maxNum(int a,int b) {
        if(a >= b) {
            return a;
        } else {
            return b;
        }
    }
}

在实现类中,所有的方法都使用了 public 访问修饰符声明。无论何时实现一个由接口定义的方法,它都必须实现为 public,因为接口中的所有成员都显式声明为 public。

(3) 最后创建测试类 NumTest,实例化接口的实现类 MathClass,调用该类中的方法并输出结果。该类内容如下:

public class NumTest {
    public static void main(String[] args) {
        // 创建实现类的对象
        MathClass calc = new MathClass(100, 300);
        System.out.println("100 和 300 相加结果是:" + calc.sum());
        System.out.println("100 比较 300,哪个大:" + calc.maxNum(100, 300));
    }
}

程序运行结果如下所示。
100 和 300 相加结果是:400
100 比较 300,哪个大:300
在该程序中,首先定义了一个 IMath 的接口,在该接口中只声明了两个未实现的方法,这两个方法需要在接口的实现类中实现。在实现类 MathClass 中定义了两个私有的属性,并赋予两个属性初始值,同时创建了该类的构造方法。因为该类实现了 MathClass 接口,因此必须实现接口中的方法。在最后的测试类中,需要创建实现类对象,然后通过实现类对象调用实现类中的方法。
在这里插入图片描述
Transport.java

package transport;

public class Transport {
	int speed;
	String name;
	
	public Transport() {
		
	}

	Transport(String name,int speed){
		this.name=name;
		this.speed=speed;
		
	}
	public void run() {
		System.out.println("交通工具在运行");
	}
}

Plane.java

package transport;
public class Plane extends Transport {
	
	public  Plane(String name,int speed) {
		super(name,speed);
	}

	public void run() {
		System.out.println(name+"飞机以每小时"+speed+"公里速度在天上飞行");
		
	}
}
class helicopter extends Plane{   //helicopter:直升飞机
	public helicopter(String name,int speed) {
		super(name,speed);
	}
	
	public void run() {
		System.out.println(name+"直升飞机以每小时"+speed+"公里速度在天上飞行");
	}
	

}

class Boeing  extends Plane{ //Boeing :波音飞机
	public Boeing (String name,int speed) {
		super(name,speed);
		
	}
	public void run() {
		System.out.println(name+"波音飞机以每小时"+speed+"公里速度在天上飞行");
	}
}

text.java

package transport;

public class text {
	public static void main(String[]args) {
		Transport transport=new Transport();
		transport=new Plane("C919",1200);
		transport.run();
		
		transport=new helicopter("AH-64 阿帕奇速度",310);
		transport.run();
		
		transport=new Boeing("波音747",940);
	    transport.run();
	}

}

作业要求:
用类的多态实现
要求编写代码实现如下功能:
1、定义一个学生类,包含学生姓名,学号,宿舍号属性。
2、定义一个教师类,包含教师姓名,职称,工资属性。
3、要求定义一个功能类,可以实现给出姓名,查找输出指定姓名的对象相关信息。(注:该姓名可能是学生类,教师类任一类的对象)
4、定义测试类测试以上功能

personal.java

public class personal {
    String name;

    public personal() {

    }

    personal(String name) {
        this.name = name;

    }

    void print(){ }

}

Student.java

public class  Student extends personal{

    String ID;
    int roomid;

    public Student(String name){
        super(name);
    }
    public Student(String name, String ID, int roomid) {
        this.name = name;
        this.ID = ID;
        this.roomid = roomid;
    }

    public void print() {
        System.out.println("通过上述查询:");
        System.out.println(name+"的学号为"+ID+"\n"+"宿舍号为"+roomid);
    }


}

teacher.java

public class teacher extends personal {

    String title;
    int wages;

    public teacher(String name) {
        super(name);

    }

    public teacher(String name, String title, int wages) {
        this.wages = wages;
        this.title = title;
        this.name = name;
    }

    public void print() {
        System.out.println("通过上述查询");
        System.out.println(name+"的职称为"+title+"\n"+"工资为"+wages);
    }
}

function.java

import java.util.Scanner;

public class function extends personal {
    public void find(){


        Scanner src = new Scanner(System.in);

        personal a = new Student("张三", "12", 111);
        personal b = new teacher("李四", "教授", 8000);

        System.out.println("请输入姓名:");
        name = src.nextLine();

        if(a.name.equals(name)){
            a.print();
        } else if(b.name.equals(name)) {
            b.print();
        } else {
            System.out.println("查无此人!");
        }
    }
}

test.java

public class test {
    public static void main(String[]args){

        function fun =new function();
        fun.find();
    }

}

//主类:三个方法,main,测试多态,测试向下转型的方法
public class Dytest {
	public static void main(String[]args) {
		A a=new A();
		B b=new B();
		C c=new C();
		System.out.println("现在开始测试多态:");
		testFunc1(a);
		testFunc1(b);
		testFunc1(c);
		System.out.println("现在开始尝试运行子类对象特有的方法:");
		testFunc2(a);
		testFunc2(b);
		testFunc2(c);
		
		// a.func1(); b.func1(); b.func2(); c.func1();
		
	}
	//测试多态 父类引用子类,实现不同的方法内容
	public static void testFunc1(A a) {
		a.func1();
	}
	//测试多态 父类引用子类,实现不同的方法内容
		public static void testFunc2(A a) {
			//a.func2();为什么不能这样做,因为A类没有func2方法,要想运行,首先需要将A类对象强制转换,转换为B类的对象
			if(a instanceof B) {
			((B)a).func2();}
			else {System.out.println("此实例不是B类的对象,不能够运行func2方法");}
		}


}

Dytest.java

class A{
	void func1() {
		System.out.println("这是A类的方法func1");
	}
}
class B extends A{
	void func1() {
		System.out.println("这是B类的方法func1");
	}
	void func2() {
		System.out.println("这是B类的方法func2");
	}
}
class C extends A{
	void func1() {
		System.out.println("这是C类的方法func1");
	}
}

shape.java
IMath.java(接口)

package demo;

public interface IMath {
	abstract void sum(int s1,int s2);
	abstract void max(int s1,int s2);
	abstract void min(int s1,int s2);
	

}

AA.java (接口)

package demo;

public class AA implements IMath{
	int num1,num2;

	@Override
	public void sum(int s1, int s2) {
		// TODO Auto-generated method stub
		System.out.println(s1+s2);
	}

	@Override
	public void max(int s1, int s2) {
		// TODO Auto-generated method stub
		if(s1>s2) {System.out.println(s1);}
		else {System.out.println(s2);}
	}

	@Override
	public void min(int s1, int s2) {
		// TODO Auto-generated method stub
		if(s1<s2) {System.out.println(s1);}
		else {System.out.println(s2);}
		
	}
	
}

测试类main

package demo;

public class Test {
	public static void main(String []args) {
		AA a=new AA();
		a.sum(15, 16);
		a.max(34, 42);
		a.min(23, 36);
	}

}

JiaDian.java

package jiadian;

public abstract class JiaDian {
	public abstract void kai();
	public abstract void guan();
	public abstract void zhuangTaiXianShi();

}

ChongDian.java

package jiadian.gongneng;

public interface ChongDian {
	public abstract void kaiShiChongDian();
	public abstract void jieShuChongDian();
	

}

DingShi.java

package jiadian.gongneng;

public interface DingShi {
	public abstract void  kaiShiDingShi(int i);
	public abstract void  zhongZhiDingShi();

}

ChongDianTaiDeng.java

package jiadian;

import jiadian.gongneng.ChongDian;

public class ChongDianTaiDeng extends JiaDian implements ChongDian{
	int KaiGuanZhuangTai;//开关状态,0关,1开
	int chongDianZhuangTai;//充电状态,0未充电,1充电中
	

	@Override
	public void kai() {
		// TODO Auto-generated method stub
		System.out.println("打开充电台灯,房间里亮了不少");
		KaiGuanZhuangTai=1;
		
	}

	@Override
	public void guan() {
		// TODO Auto-generated method stub
		System.out.println("关上充电台灯,房间里暗了不少");
		KaiGuanZhuangTai=0;
		
	}
	@Override
	public void kaiShiChongDian() {
		// TODO Auto-generated method stub
		System.out.println("台灯没电了,现在开始充电");
		chongDianZhuangTai=1;
		
	}

	@Override
	public void jieShuChongDian() {
		// TODO Auto-generated method stub
		System.out.println("电充好了,结束充电");
		chongDianZhuangTai=0;
	}
	@Override
	public void zhuangTaiXianShi() {
		// TODO Auto-generated method stub
		System.out.println("---------------");
		if(KaiGuanZhuangTai==1)System.out.println("台灯已开启");else System.out.println("台灯已关闭");
		if(chongDianZhuangTai==1)System.out.println("台灯充电中");else System.out.println("台灯未充电");
	}
	
}

十、数组

1.数组的基本概念

在这里插入图片描述
随着处理的信息量越来越大,工作也就越来越烦琐,这时可以使用数组或集合来存储信息。通过使用数组,可以在很大程度上缩短和简化程序代码,从而提高应用程序的效率。

1、数组(array)是一种最简单的复合数据类型,它是有序数据的集合,数组中的每个元素具有相同的数据类型,可以用一个统一的数组名和不同的下标来确定数组中唯一的元素。根据数组的维度,可以将其分为一维数组、二维数组和多维数组等。

2、在计算机语言中数组是非常重要的集合类型,大部分计算机语言中数组具有如下三个基本特性:
a、 一致性:数组只能保存相同数据类型元素,元素的数据类型可以是任何相同的数据类型。
b、有序性:数组中的元素是有序的,通过下标访问。
c、不可变性:数组一旦初始化,则长度(数组中元素的个数)不可变。

总的来说,数组具有以下特点:
(1)数组可以是一维数组、二维数组或多维数组。
(2)数值数组元素的默认值为 0,而引用元素的默认值为 null。
(3)数组的索引从 0 开始,如果数组有 n 个元素,那么数组的索引是从 0 到(n-1)。
3、数组元素可以是任何类型,包括数组类型。
4、数组类型是从抽象基类 Array 派生的引用类型。

注意:如果你熟悉 C/C++,请注意,Java 数组的工作原理与它们不同。

5、在 Java 中数组的下标是从零开始的,很多计算机语言的数组下标也从零开始。Java 数组下标访问运算符是中括号,如 intArray[0],表示访问 intArray 数组的第一个元素,0 是第一个元素的下标。Java 中的数组本身是引用数据类型,它的长度属性是 length。

2. 一维数组定义

在这里插入图片描述
在这里插入图片描述
1、当数组中每个元素都只带有一个下标时,这种数组就是“一维数组”。一维数组(one-dimensional array)实质上是一组相同类型数据的线性集合,是数组中最简单的一种数组。
2、数组是引用数据类型,引用数据类型在使用之前一定要做两件事情:声明和初始化。所以本文将重点介绍一维数组的创建、初始化和使用。

3. 创建一维数组

为了在程序中使用一个数组,必须声明一个引用该数组的变量,并指明整个变量可以引用的数组类型。声明一维数组的语法格式为:

type[] arrayName;    // 数据类型[] 数组名;(第一种写法)
type arrayName[];    // 数据类型 数组名[];(第二种写法)

可见数组的声明有两种形式:一种是中括号”[]“跟在元素数据类型之后,另一种是中括号”[]“跟在变量名之后。

1、对于以上两种语法格式而言,Java 更推荐采用第一种声明格式,因为第一种格式不仅具有更好的语意,而且具有更好的可读性。对于第一种格式type[] arrayName,很容易理解这是定义一个变量,其中变量名是 arrayName,而变量类型是 type[]。

2、前面已经指出:type[] 确实是一种新类型,与 type 类型完全不同(例如 int 类型是基本类型,但 int[] 是引用类型)。因此,这种方式既容易理解,也符合定义变量的语法。但第二种格式type arrayName[]的可读性就差了,看起来好像定义了一个类型为 type 的变量,而变量名是 arrayName[],这与真实的含义相去甚远。

可能有些读者非常喜欢type arrayName[]这种定义数组的方式,这可能是因为早期某些计算机读物的误导,从现在开始最好就不要再使用这种糟糕的方式了。

3、提示:Java 的模仿者 C# 就不再支持type arrayName[]这种语法,它只支持第一种定义数组的语法。越来越多的语言不再支持type arrayName[]这种数组定义语法。

以上两种格式都可以声明一个数组,其中的数据类型既可以是基本数据类型,也可以是引用数据类型。数组名可以是任意合法的变量名。声明数组就是要告诉计算机该数组中数据的类型是什么。例如:

int[] score;    // 存储学生的成绩,类型为整型
double[] price;    // 存储商品的价格,类型为浮点型
String[] name;    // 存储商品名称,类型为字符串型

在声明数组时不需要规定数组的长度,例如:

int score[10];    // 这是错误的

注意:在声明数组变量时千万不要漏写[]。

4. 分配空间

声明了数组,只是得到了一个存放数组的变量,并没有为数组元素分配内存空间,不能使用。因此要为数组分配内存空间,这样数组的每一个元素才有一个空间进行存储。

简单地说,分配空间就是要告诉计算机在内存中为它分配几个连续的位置来存储数据。在 Java 中可以使用 new 关键字来给数组分配空间。分配空间的语法格式如下:

arrayName = new type[size];    // 数组名 = new 数据类型[数组长度];

其中,数组长度就是数组中能存放的元素个数,显然应该为大于 0 的整数,例如:

score = new int[10];
price = new double[30];
name = new String[20];

这里的 score 是已经声明过的 int[] 类型的变量,当然也可以在声明数组时就给它分配空间,语法格式如下:

type[] arrayName = new type[size];    // 数据类型[] 数组名 = new 数据类型[数组长度];

例 1
例如,声明并分配一个长度为 5 的 int 类型数组 arr,代码如下:

int[] arr = new int[5];

执行后 arr 数组在内存中的格式如图 1 所示。
在这里插入图片描述

在图 1 中 arr 为数组名称,方括号“[]”中的值为数组的下标。数组通过下标来区分数组中不同的元素,并且下标是从 0 开始的。因此这里包含 5 个元素的 arr 数组最大下标为 4。

注意:一旦声明了数组的大小,就不能再修改。这里的数组长度也是必需的,不能少。

尽管数组可以存储一组基本数据类型的元素,但是数组整体属于引用数据类型。当声明一个数组变量时,其实是创建了一个类型为“数据类型[]”(如 int[]、double[]、String[])的数组对象,它具有表 1 所示的方法和属性。
在这里插入图片描述

5. 初始化一维数组

1、Java 语言中数组必须先初始化,然后才可以使用。所谓初始化,就是为数组的数组元素分配内存空间,并为每个数组元素赋初始值。
2、能不能只分配内存空间,不赋初始值呢?
不行,一旦为数组的每个数组元素分配了内存空间,每个内存空间里存储的内容就是该数组元素的值,即使这个内存空间存储的内容为空,这个空也是一个值(null)。不管以哪种方式来初始化数组,只要为数组元素分配了内存空间,数组元素就具有了初始值。3、初始值的获得有两种形式,一种由系统自动分配,另一种由程序员指定。
4、数组在初始化数组的同时,可以指定数组的大小,也可以分别初始化数组中的每一个元素。在 Java 语言中,初始化数组有以下 3 种方式。
(1)使用 new 指定数组大小后进行初始化
使用 new 关键字创建数组,在创建时指定数组的大小。语法如下:

type[] arrayName = new int[size];

创建数组之后,元素的值并不确定,需要为每一个数组的元素进行赋值,其下标从 0 开始。
例 2
创建包含 5 个元素的 int 类型的数组,然后分别将元素的值设置为 1、2、3、5 和 8。代码如下:

int[] number = new int[5];
number[0] = 1;
number[1] = 2;
number[2] = 3;
number[3] = 5;
number[4] = 8;

如果程序员只指定了数组的长度,那么系统将负责为这些数组元素分配初始值。指定初始值时,系统按如下规则分配初始值。
数组元素的类型是基本类型中的整数类型(byte、short、int 和 long),则数组元素的值是 0。
数组元素的类型是基本类型中的浮点类型(float、double),则数组元素的值是 0.0。
数组元素的类型是基本类型中的字符类型(char),则数组元素的值是‘\u0000’。
数组元素的类型是基本类型中的布尔类型(boolean),则数组元素的值是 false。
数组元素的类型是引用类型(类、接口和数组),则数组元素的值是 null。

(2) 使用 new 指定数组元素的值
使用上述方式初始化数组时,只有在为元素赋值时才确定值。可以不使用上述方式,而是在初始化时就已经确定值。语法如下:

type[] arrayName = new type[]{1,2,3,4,.....,值 n};

例 3
更改例 2 中的代码,使用 new 直接指定数组元素的值。代码如下:

int[] number = new int[]{1, 2, 3, 5, 8};

上述代码的效果等价于例 2 的效果。
注意:不要在进行数组初始化时,既指定数组的长度,也为每个数组元素分配初始值,这样会造成代码错误。例如下面代码:

int[] number = new int [5] {1,2,3,4,5};

(3) 直接指定数组元素的值
在上述两种方式的语法中,type 可以省略,如果已经声明数组变量,那么直接使用这两种方式进行初始化。如果不想使用上述两种方式,那么可以不使用 new 直接指定数组元素的值。语法如下:

type[] arrayName = {1,2,3,...,值 n};

例 4
在前面例子的基础上更改代码,直接使用上述语法实现 number 数组的初始化。代码如下:

int[] number = {1,2,3,5,8};

使用这种方式时,数组的声明和初始化操作要同步,即不能省略数组变量的类型。如下的代码就是错误的:

int[] number;
number = {1,2,3,5,8};

6. 获取单个元素

获取单个元素是指获取数组中的一个元素,如第一个元素或最后一个元素。获取单个元素的方法非常简单,指定元素所在数组的下标即可。
语法如下:arrayName[index];
其中,arrayName 表示数组变量,index 表示下标,下标为 0 表示获取第一个元素,下标为 array.length-1 表示获取最后一个元素。当指定的下标值超出数组的总长度时,会拋出 ArraylndexOutOfBoundsException 异常。
例 5
获取 number 数组中的第一个元素、最后一个元素和第六个元素,并将元素的值输出。代码如下:

int[] number = {1,2,3,5,8};
System.out.println("获取第一个元素:"+number[0]);
System.out.println("获取最后一个元素:"+number[number.length-1]);
System.out.println("获取第6个元素:"+number[5]);

执行上述代码,输出结果如下所示:
获取第一个元素:1
获取最后一个元素:8
java.lang.ArrayIndexOutOfBoundsException: 5
我们一共存入了 5 个值,所以下标的取值为 0~4。因为 number[5] 取出的内容超过了这个下标,所以输出结果会提示数组索引超出绑定异常(ArrayIndexOutOfBoundsException)。这一点是在使用数组中是经常出现的问题,大家在编写程序时应该引起注意。
课堂活动:
编写一个 Java 程序,使用数组存放录入的 5 件商品价格,然后使用下标访问第 3 个元素的值。
eg:

package deee;

import java.util.Scanner;

public class jude {
	public static void main(String []args) {
		int[] prices=new int[5];
		Scanner input =new Scanner(System.in);
		for(int i=0;i<prices.length;i++) {
			System.out.println("请输入第"+(i+1)+"件商品的价格:");
			prices[i]=input.nextInt();
		}
		System.out.println("第3件商品的价格为:"+prices[2]);
	}

}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.排序算法之冒泡排序

在这里插入图片描述
在这里插入图片描述
1、冒泡排序(Bubble Sort)是常用的数组排序算法之一,它以简洁的思想与实现方法而备受青睐,也是广大学习者最先接触的一种排序算法。

2、冒泡排序的基本思想是:对比相邻的元素值,如果满足条件就交换元素值,把较小的元素值移动到数组前面,把大的元素值移动到数组后面(也就是交换两个元素的位置),这样数组元素就像气泡一样从底部上升到顶部。

3、冒泡排序的算法比较简单,排序的结果稳定,但时间效率不太高。
Java 中的冒泡排序在双层循环中实现,其中外层循环控制排序轮数,总循环次数为要排序数组的长度减 1。而内层循环主要用于对比相邻元素的大小,以确定是否交换位置,对比和交换次数依排序轮数而减少。

例 1
获取用户在控制台输入的 5 个成绩信息,将这些成绩保存到数组中,然后对数组应用冒泡排序,并输出排序后的结果,实现步骤如下。

(1) 创建一个 Test24 类文件,在 main() 方法中开始编码。首先创建 Scanner 类的实例后声明 double 类型的 score 数组,然后接收用户在控制台输入的成绩,并保存到元素中。代码如下:

public static void main(String[] args) {
    Scanner scan = new Scanner(System.in);
    double[] score = new double[5];
    for (int i = 0; i < score.length; i++) {
        System.out.print("请输入第 " + (i + 1) + " 个成绩:");
        score[i] = scan.nextDouble();
    }
}

(2) 在对 score 数组排序之前,首先输出数组中各个元素的值。代码如下:

System.out.println("排序前的元素值:");
for(double val:score) {
    System.out.print(val+"\t");
}
System.out.println();

(3) 通过冒泡排序方法实现对 score 数组的排序,在实现时需要借助一个临时变量。代码如下:

public static void main(String[] args) {
    System.out.println("通过冒泡排序方法对数组进行排序:");
    for (int i = 0; i < score.length - 1; i++) {
        // 比较相邻两个元素,较大的数往后冒泡
        for (int j = 0; j < score.length - 1 - i; j++) {
            if (score[j] > score[j + 1]) {
                double temp = score[j + 1]; // 把第一个元素值保存到临时变量中
                score[j + 1] = score[j]; // 把第二个元素值转移到第一个元素变量中
                score[j] = temp; // 把临时变量(第一个元素的原值)保存到第二个元素中
            }
            System.out.print(score[j] + " "); // 对排序后的数组元素进行输出
        }
        System.out.print("【");
        for (int j = score.length - 1 - i; j < score.length; j++) {
            System.out.print(score[j] + " ");
        }
        System.out.println("】");
    }
}

(4) 运行前面的代码进行测试,如下所示。
请输入第 1 个成绩:77
请输入第 2 个成绩:90
请输入第 3 个成绩:68
请输入第 4 个成绩:59
请输入第 5 个成绩:80
排序前的元素值:
77.0 90.0 68.0 59.0 80.0
通过冒泡排序方法对数组进行排序:
77.0 68.0 59.0 80.0 【90.0 】
68.0 59.0 77.0 【80.0 90.0 】
59.0 68.0 【77.0 80.0 90.0 】
59.0 【68.0 77.0 80.0 90.0 】

8. 排序算法之快速排序

1、快速排序(Quicksort)是对冒泡排序的一种改进,是一种排序执行效率很高的排序算法。

快速排序的基本思想是:通过一趟排序,将要排序的数据分隔成独立的两部分,其中一部分的所有数据比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此使整个数据变成有序序列。

具体做法是:假设要对某个数组进行排序,首先需要任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它小的数都放到它的前面,所有比它大的数都放到它的后面。这个过程称为一趟快速排序;递归调用此过程,即可实现数据的快速排序。

2、练习:一个三位数加上另一个三位数,结果仍是三位数。以上三个三位数的每一位都是1-9中的一个数字且互不重复,请求出这些算式。

解题思路提示:
建立九位数数组,对应1-9.先全部置零。
1:三位数状态显示。
依次通过判断每个三位数,将数组对应每一位置1。
如x为386,先将386%10,将数组第六个元素置1;再通过386/10,将x置38,依次循环至x为0;
2:判断数组元素是否重复
如果数组有元素值为0,判定重复,否则不重复。
3:判断xyz是否重复
数组清零,然后分别对x,y,z执行状态显示操作。再执行判断数组元素是否重复操作,更根据判定结果返回布尔值。
4:寻找匹配数:数据重复判断:
外层循环x:123-987.内层循环y:x+1至987.
循环体:z。x+y结果至z,z小于987的话,判断xyz是否重复,如不重复,输出。

C++:

#include<stdio.h>
#include<math.h>
#define N 100
//使用1-9这9个数字,组成三个三位数,使两数之和等于第三个数(回溯法)
int A[N]={0};
int visited[N];
int used[N]={0};
int count=1;
int num=0;

bool check(int x,int y,int z)
{
	if(x+y==z)
		return true;
	else
		return false;
}

void solve(int step)
{
	int a,b,c;
	a=100*A[0]+10*A[1]+A[2];
	b=100*A[3]+10*A[4]+A[5];
	c=100*A[6]+10*A[7]+A[8];
	if(step==9 && check(a,b,c))
	{
        printf("%d + %d = %d  count:%d\n",a,b,c,count++);
	}
	else
	{
		for(int i=1;i<=9;i++)
		{
			if(!visited[i])
			{
				visited[i]=1;
				A[step]=i;
				solve(step+1);
				visited[i]=0;
			}
		}
	}
}

int main()
{
	solve(0);
	printf("the number of different equations:%d\n",count/2);//784+152=936等价152+784=936
}

在这里插入图片描述

java:

public class paixu {
    int x,y,z;
    int[] flagNo=new int[] {0,0,0, 0,0,0, 0,0,0};
    public static void main(String []args) {
        paixu abc=new paixu();
        abc.findRes();}
    //TODO 讲三位数对应到数组标准位中
    public void stau(int i) {
        for(int j=0;j<3;j++) {
            if(i%10!=0)flagNo[i%10-1]=1;
            i=i/10;}
    }
    //TODO 判断xyz三个三位数对应的标志位,判断是否重复
    public boolean isRepeat() {
        boolean tmp=true;//TODO true是无重复数字,false存在重复数字
        for(int j:flagNo) {
            if(j==0) tmp=false;}
        return tmp;	}

    public boolean isRepeatXYZ() {
        boolean tmp=true;//TODO true XYZ是无重复数字,falseXYZ存在重复数字
        stau(x);
        stau(y);
        stau(z);
        tmp=isRepeat();
        return tmp;
    }
    //TODO 解决问题并输出
    public void findRes() {
        int counter=0;
        for(x=123;x<=987;x++)
            for(y=x+1;y<=987;y++) {
                //TODO 状态组清零
                for(int j=0;j<flagNo.length;j++) {
                    flagNo[j]=0;
                }//TODO 实现等式
                z=x+y;
                if(z>123&&z<=987) {
                    //TODO 判断xyz中是否有重复数值
                    if(isRepeatXYZ()==true) {
                        //TODO 如果各不重复,计数加1,输出结果
                        counter++;
                        System.out.println(x+"+"+y+"="+z+"["+counter+"]");}
                }
            }
    }
}

9. 二维数组的赋值

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
课堂活动2:参照所学数组知识,完善家电案例并提交到学习通平台作业中。

十一、集合与泛型

1.集合

1.1. 集合类介绍

1、Java 提供了集合类。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。
2、Java 所有的集合类都位于 java.util 包下,提供了一个表示和操作对象集合的统一构架,包含大量集合接口,以及这些接口的实现类和操作它们的算法。
3、数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量)。
4、集合里只能保存对象(实际上只是保存对象的引用变量,但通常习惯上认为集合里保存的是对象)。
5、集合的用途:保存数量不确定的数据,以及保存具有映射关系的数据

1.2. 集合类类型

Java 集合类型分为 Collection 和 Map,它们是 Java 集合的根接口,这两个接口又包含了一些子接口或实现类。图 1 和图 2 分别为 Collection 和 Map 的子接口及其实现类。

图 1  Collection接口基本结构
图 2  Map接口基本结构
在 图 1 和图 2 中,黄色块为集合的接口,蓝色块为集合的实现类。

1.3. 接口的作用

接口名称作用
Iterator 接口集合的输出接口,主要用于遍历输出(即迭代访问)Collection 集合中的元素,Iterator 对象被称之为迭代器。迭代器接口是集合接口的父接口,实现类实现 Collection 时就必须实现 Iterator 接口。
Collection 接口是 List、Set 和 Queue 的父接口,是存放一组单值的最大接口。所谓的单值是指集合中的每个元素都是一个对象。一般很少直接使用此接口直接操作。
Queue 接口Queue 是 Java 提供的队列实现,有点类似于 List。
Dueue 接口是 Queue 的一个子接口,为双向队列。
List 接口是最常用的接口。是有序集合,允许有相同的元素。使用 List 能够精确地控制每个元素插入的位置,用户能够使用索引(元素在 List 中的位置,类似于数组下标)来访问 List 中的元素,与数组类似。
Set 接口不能包含重复的元素。不能包含重复的元素。
Map 接口是存放一对值的最大接口,即接口中的每个元素都是一对,以 key➡value 的形式保存。

对于 Set、List、Queue 和 Map 这 4 种集合,Java 最常用的实现类分别是 HashSet、TreeSet、ArrayList、ArrayDueue、LinkedList 和 HashMap、TreeMap 等。

1.4. 集合中常用的实现类

类名称作用
HashSet为优化査询速度而设计的 Set。它是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,实现比较简单
TreeSet实现了 Set 接口,是一个有序的 Set,这样就能从 Set 里面提取一个有序序列
ArrayList一个用数组实现的 List,能进行快速的随机访问,效率高而且实现了可变大小的数组
ArrayDueue是一个基于数组实现的双端队列,按“先进先出”的方式操作集合元素
LinkedList对顺序访问进行了优化,但随机访问的速度相对较慢。此外它还有 addFirst()、addLast()、getFirst()、getLast()、removeFirst() 和 removeLast() 等方法,能把它当成栈(Stack)或队列(Queue)来用
HsahMap按哈希算法来存取键对象
TreeMap可以对键对象进行排序

1、Collection 接口是 List、Set 和 Queue 接口的父接口,通常情况下不被直接使用。
2、Collection 接口定义了一些通用的方法,通过这些方法可以实现对集合的基本操作。定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。

3、Collection 接口中常用的方法。

方法名称说明
boolean add(E e)向集合中添加一个元素,如果集合对象被添加操作改变了,则返回 true。E 是元素的数据类型
boolean addAll(Collection c)向集合中添加集合 c 中的所有元素,如果集合对象被添加操作改变了,则返回 true。
void clear()清除集合中的所有元素,将集合长度变为 0。
boolean contains(Object o)判断集合中是否存在指定元素
boolean containsAll(Collection c)判断集合中是否包含集合 c 中的所有元素
boolean isEmpty()判断集合是否为空
Iteratoriterator()返回一个 Iterator 对象,用于遍历集合中的元素
boolean remove(Object o)从集合中删除一个指定元素,当集合中包含了一个或多个元素 o 时,该方法只删除第一个符合条件的元素,该方法将返回 true。
boolean removeAll(Collection c)从集合中删除所有在集合 c 中出现的元素(相当于把调用该方法的集合减去集合 c)。如果该操作改变了调用该方法的集合,则该方法返回 true。
boolean retainAll(Collection c)从集合中删除集合 c 里不包含的元素(相当于把调用该方法的集合变成该集合和集合 c 的交集),如果该操作改变了调用该方法的集合,则该方法返回 true。
int size()返回集合中元素的个数
Object[] toArray()把集合转换为一个数组,所有的集合元素变成对应的数组元素。

注意:无需硬性记忆这些方法。
集合类就像容器,现实生活中容器的功能,就是添加对象、删除对象、清空容器和判断容器是否为空等,集合类为这些功能都提供了对应的方法。
在这里插入图片描述
例 1 如何使用 Collection 接口向集合中添加方法。

public static void main(String[] args) {
    ArrayList list1 = new ArrayList(); // 创建集合 list1
    ArrayList list2 = new ArrayList(); // 创建集合 list2
    list1.add("one"); // 向 list1 添加一个元素
    list1.add("two"); // 向 list1 添加一个元素
    list2.addAll(list1); // 将 list1 的所有元素添加到 list2
    list2.add("three"); // 向 list2 添加一个元素
    System.out.println("list2 集合中的元素如下:");
    Iterator it1 = list2.iterator();
    while (it1.hasNext()) {
        System.out.print(it1.next() + "、");
    }
}

由于 Collection 是接口,不能对其实例化,所以上述代码中使用了 Collection 接口的 ArrayList 实现类来调用 Collection 的方法。
代码创建了两个集合 list1 和 list2,然后调用 add() 方法向 list1 中添加了两个元素,再调用 addAll() 方法将这两个元素添加到 list2 中。
结果如下:
list2 集合中的元素如下:
one、two、three、

import java.util.ArrayList;
import java.util.Iterator;

public class one {
    public static void main(String[]args){ //ArrayList可以方字符串、一般对象,如果存入基本数据类型,会自动装箱成对象的包装类
        ArrayList arrayList=new ArrayList();
        ArrayList arrayList2=new ArrayList();

        Cat cat1=new Cat("小猫A",1);
        Cat cat2=new Cat("小猫B",2);
        Cat cat3=new Cat("小猫C",3);
        Cat cat4=new Cat("小猫D",4);

        arrayList.add(cat1);//int??,不是int类型,事Integer类型
        arrayList.add(cat2);
        arrayList2.addAll(arrayList); //addAll
        arrayList2.add(cat3);
        arrayList2.add(cat4);
        //arrayList2.remove(cat1);//remove删除cat1
        //arrayList2.removeAll(arrayList);//remove删除

        Iterator it=arrayList2.iterator();

        while(it.hasNext()){
            //System.out.pringtln(it.next());
            ((Cat)it.next()).intro();
        }
    }
}
class Cat{
    String name;
    int age;
    public Cat(){
        name="无名";
        age=0;
    }
    public Cat(String n,int a){
        name=n;
        age=a;
    }
    public void feed(){
        System.out.println(name+"小猫喜欢吃小鱼");
    }
    public void intro(){
        System.out.println(name+"小猫喜欢吃小鱼,"+"今年"+age+"岁");
    }
}

在这里插入图片描述
例 2
Collection 集合中 size()、remove() 和 removeAll() 方法的应用。具体代码如下:

public static void main(String[] args) {
    ArrayList list1 = new ArrayList(); // 创建集合 list1
    ArrayList list2 = new ArrayList(); // 创建集合 list2
    list1.add("one");
    list1.add("two");
    list1.add("three");
    System.out.println("list1 集合中的元素数量:" + list1.size()); // 输出list1中的元素数量
    list2.add("two");
    list2.add("four");
    list2.add("six");
    System.out.println("list2 集合中的元素数量:" + list2.size()); // 输出list2中的元素数量
    list2.remove(2); // 删除第 3 个元素
    System.out.println("\nremoveAll() 方法之后 list2 集合中的元素数量:" + list2.size());
    System.out.println("list2 集合中的元素如下:");
    Iterator it1 = list2.iterator();
    while (it1.hasNext()) {
        System.out.print(it1.next() + "、");
    }
    list1.removeAll(list2);
    System.out.println("\nremoveAll() 方法之后 list1 集合中的元素数量:" + list1.size());
    System.out.println("list1 集合中的元素如下:");
    Iterator it2 = list1.iterator();
    while (it2.hasNext()) {
        System.out.print(it2.next() + "、");
    }
}

list2 集合在调用 remove(2) 方法删除第 3 个元素之后剩下了 two 和 four。list1.removeAll(list2) 语句会从 list1 中将 list1 和 list2 中相同的元素删除,即删除 two 元素。

2. List

2.1.List介绍

List 是一个有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List 集合默认按元素的添加顺序设置元素的索引,第一个添加到 List 集合中的元素的索引为 0,第二个为 1,依此类推。
List 实现了 Collection 接口,它主要有两个常用的实现类:ArrayList 类和 LinkedList 类。

2.1.1. ArrayList 类

1、ArrayList 类实现了可变数组的大小,存储在内的数据称为元素。
它还提供了快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好。
使用 ArrayList 创建的集合,允许对集合中的元素进行快速的随机访问,不过,向 ArrayList 中插入与删除元素的速度相对较慢。
ArrayList 类的常用构造方法有如下两种重载形式:
ArrayList():构造一个初始容量为 10 的空列表。
ArrayList(Collection<?extends E>c):构造一个包含指定 Collection 元素的列表,这些元素是按照该 Collection 的迭代器返回它们的顺序排列的。

ArrayList 类除了包含 Collection 接口中的所有方法之外,还包括 List 接口中提供的如表 1 所示的方法。

方法名称说明
E get(int index)获取此集合中指定索引位置的元素,E 为集合中元素的数据类型
int index(Object o)返回此集合中第一次出现指定元素的索引,如果此集合不包含该元

素,则返回 -1
int lastIndexOf(Object o) | 返回此集合中最后一次出现指定元素的索引,如果此集合不包含该
元素,则返回 -1
E set(int index, Eelement)| 将此集合中指定索引位置的元素修改为 element 参数指定的对象。
此方法返回此集合中指定索引位置的原元素
List subList(int fromlndex, int tolndex) | 返回一个新的集合,新集合中包含 fromlndex 和 tolndex 索引之间
的所有元素。包含 fromlndex 处的元素,不包含 tolndex 索引处的
元素

例 1
使用 ArrayList 类向集合中添加三个商品信息,包括商品编号、名称和价格,然后遍历集合输出这些商品信息。
(1)创建一个商品类 Product,在该类中定义 3 个属性和 toString() 方法,分别实现 setter/getter 方法。代码的实现如下:

public class Product {
    // 商品类
    private int id; // 商品编号
    private String name; // 名称
    private float price; // 价格
    public Product(int id, String name, float price) {
        this.name = name;
        this.id = id;
        this.price = price;
    }
    // 这里是上面3个属性的setter/getter方法,这里省略
    public String toString() {
        return "商品编号:" + id + ",名称:" + name + ",价格:" + price;
    }
}

(2)创建一个测试类,调用 Product 类的构造函数实例化三个对象,并将 Product 对象保存至 ArrayList 集合中。最后遍历该集合,输出商品信息。

public class Test {
    public static void main(String[] args) {
        Product pd1 = new Product(4, "木糖醇", 10);
        Product pd2 = new Product(5, "洗发水", 12);
        Product pd3 = new Product(3, "热水壶", 49);
        List list = new ArrayList(); // 创建集合
        list.add(pd1);
        list.add(pd2);
        list.add(pd3);
        System.out.println("*************** 商品信息 ***************");
        for (int i = 0; i < list.size(); i++) {
            // 循环遍历集合,输出集合元素
            Product product = (Product) list.get(i);
            System.out.println(product);
        }
    }
}

该示例中的 ArrayList 集合中存放的是自定义类 Product 的对象,这与存储的 String 类的对象是相同的。与 Set 不同的是,List 集合中存在 get() 方法,该方法可以通过索引来获取所对应的值,获取的值为 Object 类,因此需要将该值转换为 Product 类,从而获取商品信息。

例 2
在使用 List 集合时需要注意区分 indexOf() 方法和 lastIndexOf() 方法。前者是获得指定对象的最小索引位置,而后者是获得指定对象的最大索引位置。前提条件是指定的对象在 List 集合中有重复的对象,否则这两个方法获取的索引值相同。

下面的案例代码演示了 indexOf() 方法和 lastIndexOf() 方法的区别。

public static void main(String[] args) {
    List list = new ArrayList();
    list.add("One");
    list.add("|");
    list.add("Two");
    list.add("|");
    list.add("Three");
    list.add("|");
    list.add("Four");
    System.out.println("list 集合中的元素数量:" + list.size());
    System.out.println("list 集合中的元素如下:");
    Iterator it = list.iterator();
    while (it.hasNext()) {
        System.out.print(it.next() + "、");
    }
    System.out.println("\n在 list 集合中'丨'第一次出现的位置是:" + list.indexOf("|"));
    System.out.println("在 list 集合中'丨'最后一次出现的位置是:" + list.lastIndexOf("|"));
}

例 3
使用 subList() 方法截取 List 集合中部分元素时要注意,新的集合中包含起始索引位置的元素,但是不包含结束索引位置的元素。例如,subList(1,4) 方法实际截取的是索引 1 到索引 3 的元素,并组成新的 List 集合。

下面的案例代码演示了 subList() 方法的具体用法。

public static void main(String[] args) {
    List list = new ArrayList();
    list.add("One");
    list.add("Two");
    list.add("Three");
    list.add("Four");
    list.add("Five");
    list.add("Six");
    list.add("Seven");
    System.out.println("list 集合中的元素数量:" + list.size());
    System.out.println("list 集合中的元素如下:");
    Iterator it = list.iterator();
    while (it.hasNext()) {
        System.out.print(it.next() + "、");
    }
    List sublist = new ArrayList();
    sublist = list.subList(2, 5); // 从list集合中截取索引2~5的元素,保存到sublist集合中
    System.out.println("\nsublist 集合中元素数量:" + sublist.size());
    System.out.println("sublist 集合中的元素如下:");
    it = sublist.iterator();
    while (it.hasNext()) {
        System.out.print(it.next() + "、");
    }
}

2.1.2. LinkedList类

**LinkedList 类采用链表结构保存对象,**这种结构的优点是便于向集合中插入或者删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,但是 LinkedList 类随机访问元素的速度则相对较慢。这里的随机访问是指检索集合中特定索引位置的元素。

LinkedList 类除了包含 Collection 接口和 List 接口中的所有方法之外,还特别提供了表 2 所示的方法。

方法名称说明
void addFirst(E e)将指定元素添加到此集合的开头
void addLast(E e)将指定元素添加到此集合的末尾
E getFirst()返回此集合的第一个元素
E getLast()返回此集合的最后一个元素
E removeFirst()删除此集合中的第一个元素
E removeLast()删除此集合中的最后一个元素

例 4 (涉及泛型,选做)
在仓库管理系统中要记录入库的商品名称,并且需要输出第一个录入的商品名称和最后—个商品名称。下面使用 LinkedList 集合来完成这些功能,实现代码如下:

import java.util.LinkedList;

public class NewWork {
	public static void main(String[]ages) {
		LinkedList<String> products=new LinkedList<String>(); // 创建集合对象
		String p1=new String("六角螺母");
		String p2=new String("扳手A");
		String p3=new String("卷尺B");
		String p4=new String("铁钉C");
		products.add(p1); // 将 p1 对象添加到 LinkedList 集合中
		products.add(p2); // 将 p2 对象添加到 LinkedList 集合中
		products.add(p3); // 将 p3 对象添加到 LinkedList 集合中
		products.add(p4); // 将 p4 对象添加到 LinkedList 集合中
		
		String p5 = new String("标准文件夹小柜");
		products.addLast(p5); // 向集合的末尾添加p5对象
		 System.out.print("*************** 商品信息 ***************");
	        System.out.println("\n目前商品有:");
	        for (int i = 0; i < products.size(); i++) {
	            System.out.print(products.get(i) + "\t");
	        }
		
	        System.out.println("\n第一个商品的名称为:" + products.getFirst());
	        System.out.println("最后一个商品的名称为:" + products.getLast());
	        products.removeLast(); // 删除最后一个元素
	        System.out.println("删除最后的元素,目前商品有:");
	        for (int i = 0; i < products.size(); i++) {
	            System.out.print(products.get(i) + "\t");
	        }
	    }
	}
import java.util.LinkedList;

public class NewWork2 {
	public static void main(String[]ages) {
		LinkedList<Test2> products=new LinkedList<Test2>(); // 创建集合对象
		Test2 p1=new Test2("六角螺母");
		Test2 p2=new Test2("扳手A");
		Test2 p3=new Test2("卷尺B");
		Test2 p4=new Test2("铁钉C");
		products.add(p1); // 将 p1 对象添加到 LinkedList 集合中
		products.add(p2); // 将 p2 对象添加到 LinkedList 集合中
		products.add(p3); // 将 p3 对象添加到 LinkedList 集合中
		products.add(p4); // 将 p4 对象添加到 LinkedList 集合中
		products.get(2).intr();
		
	        }
	    }

class Test2{
	String name;
	
	public Test2(String n) {
		name=n;
	}
	public void intr() {
		System.out.println("产品名称:"+name);
		
	}
}
	
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;

public class NewWork2 {
	public static void main(String[]ages) {
		HashMap hashMap=new HashMap();
		hashMap.put(111222, "张三");
		
		Iterator iterator=hashMap.keySet().iterator();
		while(iterator.hasNext()) {
			System.out.println(iterator.next());
		}
		
	
	        }
	    }

class Test2{
	String name;
	
	public Test2(String n) {
		name=n;
	}
	public void intr() {
		System.out.println("产品名称:"+name);
		
	}
}

2. Set

Set 集合类似于一个罐子,程序可以依次把多个对象“丢进”Set 集合,而 Set 集合通常不能记住元素的添加顺序。也就是说 Set 集合中的对象不按特定的方式排序,只是简单地把对象加入集合。Set 集合中不能包含重复的对象,并且最多只允许包含一个 null 元素。

Set 实现了 Collection 接口,它主要有两个常用的实现类:HashSet 类和 TreeSet类

2.1. HashSet 类介绍

HashSet 是按照 Hash 算法来存储集合中的元素。因此具有很好的存取和查找性能。

HashSet 具有以下特点:
1、不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
2、HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步。
3、集合元素值可以是 null。
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals() 方法比较返回的结果为 true,但它们的 hashCode 不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。
也就是说,两个对象的 hashCode 值相等且通过 equals() 方法比较返回结果为 true,则 HashSet 集合认为两个元素相等。

在 HashSet 类中实现了 Collection 接口中的所有方法。HashSet 类的常用构造方法重载形式如下。
1、HashSet():构造一个新的空的 Set 集合。
2、HashSet(Collection<? extends E>c):构造一个包含指定 Collection 集合元素的新 Set 集合。其中,“< >”中的 extends 表示
3、HashSet 的父类,即指明该 Set 集合中存放的集合元素类型。c 表示其中的元素将被存放在此 Set 集合中。

下面的代码演示了创建两种不同形式的HashSet 对象。

HashSet hs = new HashSet();    // 调用无参的构造函数创建HashSet对象
HashSet<String> hss = new HashSet<String>();    // 创建泛型的 HashSet 集合对象

例 1
编写一个 Java 程序,使用 HashSet 创建一个 Set 集合,并向该集合中添加 4 套教程。具体实现代码如下:

public static void main(String[] args) {
    HashSet<String> courseSet = new HashSet<String>(); // 创建一个空的 Set 集合
    String course1 = new String("Java入门教程");
    String course2 = new String("Python基础教程");
    String course3 = new String("C语言学习教程");
    String course4 = new String("Golang入门教程");
    courseSet.add(course1); // 将 course1 存储到 Set 集合中
    courseSet.add(course2); // 将 course2 存储到 Set 集合中
    courseSet.add(course3); // 将 course3 存储到 Set 集合中
    courseSet.add(course4); // 将 course4 存储到 Set 集合中
    System.out.println("C语言中文网教程有:");
    Iterator<String> it = courseSet.iterator();
    while (it.hasNext()) {
        System.out.println("《" + (String) it.next() + "》"); // 输出 Set 集合中的元素
    }
    System.out.println("有" + courseSet.size() + "套精彩教程!");
}

注意:在以上示例中,如果再向 CourseSet 集合中再添加一个名称为“Java入门教程”的 String 对象,则输出的结果与上述执行结果相同。也就是说,如果向 Set 集合中添加两个相同的元素,则后添加的会覆盖前面添加的元素,即在 Set 集合中不会出现相同的元素。

2.2. TreeSet 类介绍

TreeSet 类同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以实现对集合进行自然排序,因此使用 TreeSet 类实现的 Set 接口默认情况下是自然排序的,这里的自然排序指的是升序排序。

TreeSet 只能对实现了 Comparable 接口的类对象进行排序,因为 Comparable 接口中有一个 compareTo(Object o) 方法用于比较两个对象的大小。例如 a.compareTo(b),如果 a 和 b 相等,则该方法返回 0;如果 a 大于 b,则该方法返回大于 0 的值;如果 a 小于 b,则该方法返回小于 0 的值。

3.Map

Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含一个键(key)对象和一个值(value)对象。用于保存具有映射关系的数据。

Map 集合里保存着两组值,一组值用于保存 Map 里的 key,另外一组值用于保存 Map 里的 value,**key 和 value 都可以是任何引用类型的数据。**Map 的 key 不允许重复,value 可以重复,即同一个 Map 对象的任何两个 key 通过 equals 方法比较总是返回 false。

Map 中的 key 和 value 之间存在单向一对一关系,即通过指定的 key,总能找到唯一的、确定的 value。从 Map 中取出数据时,只要给出指定的 key,就可以取出对应的 value。

Map 接口主要有两个实现类:HashMap 类和 TreeMap 类。其中,HashMap 类按哈希算法来存取键对象,而 TreeMap 类可以对键对象进行排序。
Map 集合最典型的用法就是成对地添加、删除 key-value 对,接下来即可判断该 Map 中是否包含指定 key,也可以通过 Map 提供的 keySet() 方法获取所有 key 组成的集合,进而遍历 Map 中所有的 key-value 对。下面程序示范了 Map 的基本功能。
例 1
每名学生都有属于自己的唯一编号,即学号。在毕业时需要将该学生的信息从系统中移除。

下面编写 Java 程序,使用 HashMap 来存储学生信息,其键为学生学号,值为姓名。毕业时,需要用户输入学生的学号,并根据学号进行删除操作。具体的实现代码如下:

public class Test09 {
    public static void main(String[] args) {
        HashMap users = new HashMap();
        users.put("11", "张浩太"); // 将学生信息键值对存储到Map中
        users.put("22", "刘思诚");
        users.put("33", "王强文");
        users.put("44", "李国量");
        users.put("55", "王路路");
        System.out.println("******** 学生列表 ********");
        Iterator it = users.keySet().iterator();
        while (it.hasNext()) {
            // 遍历 Map
            Object key = it.next();
            Object val = users.get(key);
            System.out.println("学号:" + key + ",姓名:" + val);
        }
        Scanner input = new Scanner(System.in);
        System.out.println("请输入要删除的学号:");
        int num = input.nextInt();
        if (users.containsKey(String.valueOf(num))) { // 判断是否包含指定键
            users.remove(String.valueOf(num)); // 如果包含就删除
        } else {
            System.out.println("该学生不存在!");
        }
        System.out.println("******** 学生列表 ********");
        it = users.keySet().iterator();
        while (it.hasNext()) {
            Object key = it.next();
            Object val = users.get(key);
            System.out.println("学号:" + key + ",姓名:" + val);
        }
    }
}

注意:TreeMap 类的使用方法与 HashMap 类相同,唯一不同的是 TreeMap 类可以对键对象进行排序

4. 泛型

集合有个缺点,就是把一个对象“丢进”集合里之后,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了 Object 类型(其运行时类型没变)。

Java 集合之所以被设计成这样,是因为集合的设计者不知道我们会用集合来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要求具有很好的通用性,但这样做带来如下两个问题:
集合对元素类型没有任何限制,这样可能引发一些问题。例如,想创建一个只能保存 Dog 对象的集合,但程序也可以轻易地将 Cat 对象“丢”进去,所以可能引发异常。
由于把对象“丢进”集合时,集合丢失了对象的状态信息,集合只知道它盛装的是 Object,因此取出集合元素后通常还需要进行强制类型转换。这种强制类型转换既增加了编程的复杂度,也可能引发 ClassCastException 异常。

所以为了解决上述问题,从 Java 1.5 开始提供了泛型。泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率。

4.1. 泛型集合

**泛型本质上是提供类型的“类型参数”,也就是参数化类型。**我们可以为类、接口或方法指定一个类型参数,通过这个参数限制操作的数据类型,从而保证类型转换的绝对安全。
例 1
下面将结合泛型与集合编写一个案例实现图书信息输出。
(1)首先需要创建一个表示图书的实体类 Book,其中包括的图书信息有图书编号、图书名称和价格。Book 类的具体代码如下:

public class Book {
    private int Id; // 图书编号
    private String Name; // 图书名称
    private int Price; // 图书价格
    public Book(int id, String name, int price) { // 构造方法
        this.Id = id;
        this.Name = name;
        this.Price = price;
    }
    public String toString() { // 重写 toString()方法
        return this.Id + ", " + this.Name + "," + this.Price;
    }
}

(2)使用 Book 作为类型创建 Map 和 List 两个泛型集合,然后向集合中添加图书元素,最后输出集合中的内容。具体代码如下:

public class Test14 {
    public static void main(String[] args) {
        // 创建3个Book对象
        Book book1 = new Book(1, "唐诗三百首", 8);
        Book book2 = new Book(2, "小星星", 12);
        Book book3 = new Book(3, "成语大全", 22);
        Map<Integer, Book> books = new HashMap<Integer, Book>(); // 定义泛型 Map 集合
        books.put(1001, book1); // 将第一个 Book 对象存储到 Map 中
        books.put(1002, book2); // 将第二个 Book 对象存储到 Map 中
        books.put(1003, book3); // 将第三个 Book 对象存储到 Map 中
        System.out.println("泛型Map存储的图书信息如下:");
        for (Integer id : books.keySet()) {
            // 遍历键
            System.out.print(id + "——");
            System.out.println(books.get(id)); // 不需要类型转换
        }
        List<Book> bookList = new ArrayList<Book>(); // 定义泛型的 List 集合
        bookList.add(book1);
        bookList.add(book2);
        bookList.add(book3);
        System.out.println("泛型List存储的图书信息如下:");
        for (int i = 0; i < bookList.size(); i++) {
            System.out.println(bookList.get(i)); // 这里不需要类型转换
        }
    }
}

在该示例中,第 7 行代码创建了一个键类型为 Integer、值类型为 Book 的泛型集合,即指明了该 Map 集合中存放的键必须是 Integer 类型、值必须为 Book 类型,否则编译出错。在获取 Map 集合中的元素时,不需要将books.get(id);获取的值强制转换为 Book 类型,程序会隐式转换。在创建 List 集合时,同样使用了泛型,因此在获取集合中的元素时也不需要将bookList.get(i)代码强制转换为 Book 类型,程序会隐式转换。
执行结果如下:
泛型Map存储的图书信息如下:
1001——1, 唐诗三百首,8
1003——3, 成语大全,22
1002——2, 小星星,12
泛型List存储的图书信息如下:
1, 唐诗三百首,8
2, 小星星,12
3, 成语大全,22
在实例化泛型类时,需要指明泛型类中的类型参数,并赋予泛型类属性相应类型的值。


public class Stu<A> { //泛型标志,泛型类
	String name;
	A age;
	
	public Stu() {
		
	}
	private  Stu(String n,A a) {
		name=n;
		age=a;
	}
	public A getAge() {
		return age;
	}
	

	
	public static void main(String[]ages) {
		Stu<Integer> abc=new Stu<Integer>("ABC",19);
		Stu<String> bcd=new Stu<String>("ABC","十九岁");
		System.out.println(abc.getAge());
		System.out.println(bcd.getAge());
	}
}

结果为
19
十九岁

4.2.泛型方法

到目前为止,我们所使用的泛型都是应用于整个类上。泛型同样可以在类中包含参数化的方法,而方法所在的类可以是泛型类,也可以不是泛型类。也就是说,是否拥有泛型方法,与其所在的类是不是泛型没有关系。

泛型方法使得该方法能够独立于类而产生变化。如果使用泛型方法可以取代类泛型化,那么就应该只使用泛型方法。另外,对一个 static 的方法而言,无法访问泛型类的类型参数。因此,如果 static 方法需要使用泛型能力,就必须使其成为泛型方法。

定义泛型方法的语法格式如下:
[访问权限修饰符] [static] [final] <类型参数列表> 返回值类型 方法名([形式参数列表])
例如:

public static <T> List find(Class<T> cs,int userId){}

一般来说编写 Java 泛型方法,其返回值类型至少有一个参数类型应该是泛型,而且类型应该是一致的,如果只有返回值类型或参数类型之一使用了泛型,那么这个泛型方法的使用就被限制了。下面就来定义一个泛型方法,具体介绍泛型方法的创建和使用。


public class Stu<A,T> { //泛型标志,泛型类
	//学号,可能是一串数字(6位),也有可能包含字母的,不同学校的编码方式不同
	T stuNo;
	String name;
	A age;
	
	public Stu() {
		
	}
	private  Stu(String n,A a,T s) {
		name=n;
		age=a;
		stuNo=s;
	}
	public A getAge() {
		return age;
	}

	
	public static void main(String[]ages) {
		Stu<Integer,Integer> abc=new Stu<Integer,Integer>("ABC",19,123456);
		Stu<String,String> bcd=new Stu<String,String>("ABC","十九岁","S202108");
		System.out.println(abc.getAge());
		System.out.println(bcd.getAge());
	}
}

结果为
19
十九岁


public class Stu<A,T> { //泛型标志,泛型类
	//学号,可能是一串数字(6位),也有可能包含字母的,不同学校的编码方式不同
	T stuNo;
	String name;
	A age;
	
	public Stu() {
		
	}
	private  Stu(String n,A a,T s) {
		name=n;
		age=a;
		stuNo=s;
	}
	public A getAge() {
		return age;
	}

	public T getstuNo() {
		return stuNo;
	}
	public static void main(String[]ages) {
		Stu<Integer,Integer> abc=new Stu<Integer,Integer>("ABC",19,123456);
		Stu<String,String> bcd=new Stu<String,String>("ABC","十九岁","S202108");
		System.out.println(abc.getAge());
		System.out.println(bcd.getAge());
		System.out.println(abc.getstuNo());
		System.out.println(bcd.getstuNo());
	}
}

结果为
19
十九岁
123456
S202108

十二、异常

**异常(exception)是在运行程序时产生的一种异常情况。异常又称为例外,是一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流。**为了能够及时有效地处理程序中的运行错误,必须使用异常类,这可以让程序具有极好的容错性且更加健壮。

在 Java 中一个异常的产生,主要有如下三种原因:
Java 内部错误发生异常,Java 虚拟机产生的异常。
编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等。
通过 throw 语句手动生成的异常,一般用来告知该方法的调用者一些必要信息。

Java 通过面向对象的方法来处理异常。在一个方法的运行过程中,如果发生了异常,则这个方法会产生代表该异常的一个对象,并把它交给运行时的系统,运行时系统寻找相应的代码来处理这一异常。

我们把生成异常对象,并把它提交给运行时系统的过程称为拋出(throw)异常。运行时系统在方法的调用栈中查找,直到找到能够处理该类型异常的对象,这一个过程称为捕获(catch)异常。

1. 异常类型

为了能够及时有效地处理程序中的运行错误,Java 专门引入了异常类。在 Java 中所有异常类型都是内置类 java.lang.Throwable 类的子类,即 Throwable 位于异常类层次结构的顶层。Throwable 类下有两个异常分支 Exception 和 Error,如图 1 所示。
在这里插入图片描述

由图 2 可以知道,**Throwable 类是所有异常和错误的超类,下面有 Error 和 Exception 两个子类分别表示错误和异常。**其中异常类 Exception 又分为运行时异常和非运行时异常,这两种异常有很大的区别,也称为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。
Exception 类用于用户程序可能出现的异常情况,它也是用来创建自定义异常类型类的类。
Error 定义了在通常环境下不希望被程序捕获的异常。一般指的是 JVM 错误,如堆栈溢出。

public class NewDemo {
	int a,b;
	
	public NewDemo(int c,int d) {
		a=c;
		b=d;
	}
	public void intro() {
		System.out.println(a/b);
	}
	
	public static void main(String[]args) {
		NewDemo c=new NewDemo(128,0);
		c.intro();
	}
}


在这里插入图片描述
Java中的数组下标越界问题是说明超出范围
数组下标以0开始


public class NewDemo {
	public static void main(String[]args) {
		int[] i= {1,2};
		System.out.println(i[2]);
		
	}
}

在这里插入图片描述

空指针异常:解释是"程序遇上了空指针"。简单地说就是调用了未经初始化的对象或者是不存在的对象,这个错误经常出现在创建图片,调用数组这些操作中,比如图片未经初始化,或者图片创建时的路径错误等等。对数组操作中出现空指针。数组的初始化是对数组分配需要的空间,而初始化后的数组,其中的元素并没有实例化,依然是空的,所以还需要对每个元素都进行初始化


public class NewDemo {
	public static void main(String[]args) {
		String name=null;
		System.out.println(name.charAt(3));
		
	}
}

在这里插入图片描述
例 1
下面的示例代码实现了允许用户输入 1~3 以内的整数,其他情况提示输入错误。

import java.util.Scanner;

public class NewDemo {
	public static void main(String[]args) {
		int a;
		System.out.println("请输入数字1-3:");
		Scanner scanner=new Scanner(System.in);
		a=scanner.nextInt();
		switch(a) {
		case 1:
			System.out.println("你输入的值是1");
			break;
		case 2:
			System.out.println("你输入的值是2");
			break;
		case 3:
			System.out.println("你输入的值是3");
			break;
		default:
			System.out.println("你输入的值不是1-3,请检查输入的值。");
			break;
		}
		
	}
}

public class NewDemo {
	public static void main(String[]args) {
		int i=15;
		try {
			System.out.println(i/5);
		}catch(ArithmeticException e) {
			System.out.println("不能执行被零除操作");
		}
			
	}
}

结果为3

import java.util.Scanner;

public class NewDemo {
	public static void main(String[]args) {
		int [] i= {1,2};
		try {
			System.out.println(i[10]);
		}catch(ArithmeticException e) {
			System.out.println("不能执行被零除操作");
		}
			
	}
}

2. Error和Exception的异同

Error(错误)和 Exception(异常)都是 java.lang.Throwable 类的子类,在 Java 代码中只有继承了 Throwable 类的实例才能被 throw 或者 catch。

Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类,Exception 是程序正常运行过程中可以预料到的意外情况,并且应该被开发者捕获,进行相应的处理。Error 是指正常情况下不大可能出现的情况,绝大部分的 Error 都会导致程序处于非正常、不可恢复状态。所以不需要被开发者捕获。

Error 错误是任何处理技术都无法恢复的情况,肯定会导致程序非正常终止。并且 Error 错误属于未检查类型,大多数发生在运行时。Exception 又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源码里必须显示的进行捕获处理,这里是编译期检查的一部分。不检查异常就是所谓的运行时异常,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译器强制要求。

如下是常见的 Error 和 Exception:
(1)运行时异常(RuntimeException):
NullPropagation:空指针异常;
ClassCastException:类型强制转换异常
IllegalArgumentException:传递非法参数异常
IndexOutOfBoundsException:下标越界异常
NumberFormatException:数字格式异常

(2)非运行时异常:
ClassNotFoundException:找不到指定 class 的异常
IOException:IO 操作异常

(3)错误(Error):
NoClassDefFoundError:找不到 class 定义异常
StackOverflowError:深递归导致栈被耗尽而抛出的异常
OutOfMemoryError:内存溢出异常
例 3
下面代码会导致 Java 堆栈溢出错误。

// 通过无限递归演示堆栈溢出错误
class StackOverflow {
    public static void test(int i) {
        if (i == 0) {
            return;
        } else {
            test(i++);
        }
    }
}
public class ErrorEg {
    public static void main(String[] args) {
        // 执行StackOverflow方法
        StackOverflow.test(5);
    }
}

运行输出为:
Exception in thread “main” java.lang.StackOverflowError
at ch11.StackOverflow.test(ErrorEg.java:9)
at ch11.StackOverflow.test(ErrorEg.java:9)
at ch11.StackOverflow.test(ErrorEg.java:9)
at ch11.StackOverflow.test(ErrorEg.java:9)
上面代码通过无限递归调用最终引发了 java.lang.StackOverflowError 错误。

3. 异常处理机制及异常处理的基本结构

异常处理通过 5 个关键字来实现:try、catch、throw、throws 和 finallytry catch 语句用于捕获并处理异常,finally 语句用于在任何情况下(除特殊情况外)都必须执行的代码,throw 语句用于拋出异常,throws 语句用于声明可能会出现的异常。
Java 的异常处理机制提供了一种结构性和控制性的方式来处理程序执行期间发生的事件。异常处理的机制如下:
1、在方法中用 try catch 语句捕获并处理异常,catch 语句可以有多个,用来匹配多个异常。
2、对于处理不了的异常或者要转型的异常,在方法的声明处通过 throws 语句拋出异常,即由上层的调用方法来处理。

以下代码是异常处理程序的基本结构:

try {
    逻辑程序块
} catch(ExceptionType1 e) {
    处理代码块1
} catch (ExceptionType2 e) {
    处理代码块2
    throw(e);    // 再抛出这个"异常"
} finally {
    释放资源代码块
}Java 中通常采用 try catch 语句来捕获异常并处理。语法格式如下:
try {
    // 可能发生异常的语句
} catch(ExceptionType e) {
    // 处理异常语句
}

在以上语法中,把可能引发异常的语句封装在 try 语句块中,用以捕获可能发生的异常。catch 后的( )里放匹配的异常类,指明 catch 语句可以处理的异常类型,发生异常时产生异常类的实例化对象。

如果 try 语句块中发生异常,那么一个相应的异常对象就会被拋出,然后 catch 语句就会依据所拋出异常对象的类型进行捕获,并处理。处理之后,程序会跳过 try 语句块中剩余的语句,转到 catch 语句块后面的第一条语句开始执行。

如果 try 语句块中没有异常发生,那么 try 块正常结束,后面的 catch 语句块被跳过,程序将从 catch 语句块后的第一条语句开始执行。

注意:try…catch 与 if…else 不一样,try 后面的花括号{ }不可以省略,即使 try 块里只有一行代码,也不可省略这个花括号。与之类似的是,catch 块后的花括号{ }也不可以省略。另外,try 块里声明的变量只是代码块内的局部变量,它只在 try 块内有效,其它地方不能访问该变量。

在上面语法的处理代码块 1 中,可以使用以下 3 个方法输出相应的异常信息。
printStackTrace() 方法:指出异常的类型、性质、栈层次及出现在程序中的位置(关于 printStackTrace 方法的使用可参考《Java的异常跟踪栈》一节)。
getMessage() 方法:输出错误的性质。
toString() 方法:给出异常的类型与性质。

4. 多重catch语句

如果 try 代码块中有很多语句会发生异常,而且发生的异常种类又很多。那么可以在 try 后面跟有多个 catch 代码块。多 catch 代码块语法如下:

try {
    // 可能会发生异常的语句
} catch(ExceptionType e) {
    // 处理异常语句
} catch(ExceptionType e) {
    // 处理异常语句
} catch(ExceptionType e) {
    // 处理异常语句
...
}

在多个 catch 代码块的情况下,当一个 catch 代码块捕获到一个异常时,其它的 catch 代码块就不再进行匹配。

注意:当捕获的多个异常类之间存在父子关系时,捕获异常时一般先捕获子类,再捕获父类。所以子类异常必须在父类异常的前面,否则子类捕获不到。

5. throws和throw:声明和抛出异常

异常处理除了捕获异常和处理异常之外,还包括声明异常和拋出异常。实现声明和抛出异常的关键字非常相似,它们是 throws 和 throw。可以通过 throws 关键字在方法上声明该方法要拋出的异常,然后**在方法内部通过 throw 拋出异常对象。**本节详细介绍在 Java 中如何声明异常和拋出异常。

5.1. throws 声明异常

当一个方法产生一个它不处理的异常时,那么就需要在该方法的头部声明这个异常,以便将该异常传递到方法的外部进行处理。使用 throws 声明的方法表示此方法不处理异常。throws 具体格式如下:

returnType method_name(paramList) throws Exception 1,Exception2,{}

其中,returnType 表示返回值类型;method_name 表示方法名;paramList 表示参数列表;Exception 1,Exception2,… 表示异常类。
**如果有多个异常类,它们之间用逗号分隔。**这些异常类可以是方法中调用了可能拋出异常的方法而产生的异常,也可以是方法体中生成并拋出的异常。
使用 throws 声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由向上一级的调用者处理;如果 main 方法也不知道如何处理这种类型的异常,也可以使用 throws 声明抛出异常,该异常将交给 JVM 处理。JVM 对异常的处理方法是,打印异常的跟踪栈信息,并中止程序运行,这就是前面程序在遇到异常后自动结束的原因。
方法重写时声明抛出异常的限制
使用 throws 声明抛出异常时有一个限制,是方法重写中的一条规则:子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,子类方法声明抛出的异常不允许比父类方法声明抛出的异常多。

5.2. throw 拋出异常

与 throws 不同的是,throw 语句用来直接拋出一个异常,后接一个可拋出的异常类对象,其语法格式如下:
throw ExceptionObject;

其中,ExceptionObject 必须是 Throwable 类或其子类的对象。如果是自定义异常类,也必须是 Throwable 的直接或间接子类。例如,以下语句在编译时将会产生语法错误:
throw new String(“拋出异常”); // String类不是Throwable类的子类

当 throw 语句执行时,它后面的语句将不执行,此时程序转向调用者程序,寻找与之相匹配的 catch 语句,执行相应的异常处理程序。如果没有找到相匹配的 catch 语句,则再转向上一层的调用程序。这样逐层向上,直到最外层的异常处理程序终止程序并打印出调用栈情况。

throw 关键字不会单独使用,它的使用完全符合异常的处理机制,但是,一般来讲用户都在避免异常的产生,所以不会手工抛出一个新的异常类的实例,而往往会抛出程序中已经产生的异常类的实例。
例 2
在某仓库管理系统中,要求管理员的用户名需要由 8 位以上的字母或者数字组成,不能含有其他的字符。当长度在 8 位以下时拋出异常,并显示异常信息;当字符含有非字母或者数字时,同样拋出异常,显示异常信息。代码如下:

import java.util.Scanner;
public class Test05 {
    public boolean validateUserName(String username) {
        boolean con = false;
        if (username.length() > 8) {
            // 判断用户名长度是否大于8位
            for (int i = 0; i < username.length(); i++) {
                char ch = username.charAt(i); // 获取每一位字符
                if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
                    con = true;
                } else {
                    con = false;
                    throw new IllegalArgumentException("用户名只能由字母和数字组成!");
                }
            }
        } else {
            throw new IllegalArgumentException("用户名长度必须大于 8 位!");
        }
        return con;
    }
    public static void main(String[] args) {
        Test05 te = new Test05();
        Scanner input = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = input.next();
        try {
            boolean con = te.validateUserName(username);
            if (con) {
                System.out.println("用户名输入正确!");
            }
        } catch (IllegalArgumentException e) {
            System.out.println(e);
        }
    }
}

6.自定义异常

自定义异常的语法形式为:

<class><自定义异常名><extends><Exception>

自定义异常类一般包含两个构造方法:一个是无参的默认构造方法,另一个构造方法以字符串的形式接收一个定制的异常消息,并将该消息传递给超类的构造方法。

例如,以下代码创建一个名称为 IntegerRangeException 的自定义异常类:

class IntegerRangeException extends Exception {
    public IntegerRangeException() {
        super();
    }
    public IntegerRangeException(String s) {
        super(s);
    }
}

以上代码创建的自定义异常类 IntegerRangeException 类继承自 Exception 类,在该类中包含两个构造方法。
例 1
编写一个程序,对会员注册时的年龄进行验证,即检测是否在 0~100 岁。
1)这里创建了一个异常类 MyException,并提供两个构造方法。MyException 类的实现代码如下:

public class MyException extends Exception {
    public MyException() {
        super();
    }
    public MyException(String str) {
        super(str);
    }
}

2)接着创建测试类,调用自定义异常类。代码实现如下:

import java.util.InputMismatchException;
import java.util.Scanner;
public class Test07 {
    public static void main(String[] args) {
        int age;
        Scanner input = new Scanner(System.in);
        System.out.println("请输入您的年龄:");
        try {
            age = input.nextInt();    // 获取年龄
            if(age < 0) {
                throw new MyException("您输入的年龄为负数!输入有误!");
            } else if(age > 100) {
                throw new MyException("您输入的年龄大于100!输入有误!");
            } else {
                System.out.println("您的年龄为:"+age);
            }
        } catch(InputMismatchException e1) {
            System.out.println("输入的年龄不是数字!");
        } catch(MyException e2) {
            System.out.println(e2.getMessage());
        }
    }
}

3)运行该程序,当用户输入的年龄为负数时,则拋出 MyException 自定义异常,执行第二个 catch 语句块中的代码,打印出异常信息。程序的运行结果如下所示。
请输入您的年龄:
-2
您输入的年龄为负数!输入有误!

当用户输入的年龄大于 100 时,也会拋出 MyException 自定义异常,同样会执行第二个 catch 语句块中的代码,打印出异常信息,如下所示。
请输入您的年龄:
110
您输入的年龄大于100!输入有误!

在该程序的主方法中,使用了 if…else if…else 语句结构判断用户输入的年龄是否为负数和大于 100 的数,如果是,则拋出自定义异常 MyException,调用自定义异常类 MyException 中的含有一个 String 类型的构造方法。在 catch 语句块中捕获该异常,并调用 getMessage() 方法输出异常信息。

提示:因为自定义异常继承自 Exception 类,因此自定义异常类中包含父类所有的属性和方法。
在这里插入图片描述


public class demo {
	public static void main(String[]args) {
		try {
			int[]c={1,2,1};
			c[3]=3;
			String b=null;
			System.out.println(b.charAt(0));
		}catch(NullPointerException e) {
			e.printStackTrace();//现在由空指针异常对象在处理异常
		}catch(Exception e) {
			e.printStackTrace();//现在由异常对象在处理异常
		}
	}

}

结果:
java.lang.ArrayIndexOutOfBoundsException: 3
at demo.main(demo.java:6)

注意:Exception 是数组下标越界的父类,子类在前,父类在后


public class demo {
	public static void main(String[]args) {
		try {
			int[]c={1,2,1};
			c[3]=3;
			String b=null;
			System.out.println(b.charAt(0));
		}catch(NullPointerException e) {
			e.printStackTrace();//现在由空指针异常对象在处理异常
		}catch(ArithmeticException e) {
			e.printStackTrace();//现在由算术运算异常对象在处理异常
		}
	}

}

结果:Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 3
at demo.main(demo.java:6)
注意:ArithmeticException是算术运算异常,NullPointerException是空指针异常,而代码的异常是数组下标越界,无法匹配异常,系统自动处理。

throw和throws异常实例


public class Example {
	static void throwProcess() {
		try {
			throw new Exception("测试程序抛出了一个异常");
		}catch (Exception e) {
			System.out.println("\n在throwProcess中捕获"+e.getMessage());
		}
	}
	public static void main(String[] args) {
		throwProcess();
	}

}

在这里插入图片描述


public class Example {
	static void throwProcess() throws Exception  {
		try {
			throw new Exception("测试程序抛出了一个异常");
		}catch (Exception e) {
			System.out.println("\n在throwProcess中捕获"+e.getMessage());
		
		}
	}
	public static void main(String[] args) {
		try {
			throwProcess();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			System.out.println("\n在main 中捕获"+e.getMessage());
		}
	}

}

结果为
在throwProcess中捕获测试程序抛出了一个异常
解析:程序抛出一个异常,catch抓住一个异常并处理,然后输出一个结果


public class Example {
	static void throwProcess() throws Exception  {
		try {
			throw new Exception("测试程序抛出了一个异常");
		}catch (Exception e) {
			System.out.println("\n在throwProcess中捕获"+e.getMessage());
			throw e;
		}
	}
	public static void main(String[] args) {
		try {
			throwProcess();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			System.out.println("\n在main 中捕获"+e.getMessage());
		}
	}

}

结果:
在throwProcess中捕获测试程序抛出了一个异常
在main 中捕获测试程序抛出了一个异常
解析:
程序多一个throw e,因为在处理异常的时候,又同时抛出一个异常,所以下面处理抛出的异常

import java.util.Scanner;

import javax.security.auth.login.LoginException;

public class test {
	public boolean valldateLogin(String username,String pwd) {
		boolean con=false;
		boolean conUname=false;
		try {
			if(username.length()>=6 && username.length()<=10) {
				for(int i=0;i<username.length();i++) {
					char ch = username.charAt(i);
					if(ch>='0'&& ch<='9') {
						conUname=false;
						throw new LoginException("用户名中包含有非数字的字符!");
					}
				}
			}else {
				throw new LoginException("用户长度必须在6~10位之间!");
			}
			if(conUname) {
				if(pwd.length()==6) {
					con=true;
				}else {
					con=false;
					throw new LoginException("密码长度必须为6位!");
				}
			}
		}catch(LoginException e) {
			System.out.println(e.getMessage());
		}
		  return con;
		
	}
	public static void main(String[] args) {
		Scanner input=new Scanner(System.in);
		System.out.println("用户名:");
		String username=input.next();
		System.out.println("密码:");
		String password=input.next();
		test it=new test();
		boolean con=it.valldateLogin(username, password);
		if(con) {
			System.out.println("登录成功!");
		}
	}

}

public class LoginEcxeption extends Exception{
	
        public LoginException(){
        	super();
        }
        public LoginException(String msg) {
        	super(msg);
        }

}

在本程序的 validateLogin() 方法中使用条件控制语句和 for 循环语句分别对用户名和密码进行了验证。任何不符合用户名或者密码要求的情况都拋出自定义异常 LoginException,并在 catch 语句中捕获该异常,输出异常信息。

运行程序,当用户输入的用户名含有非数字字符时将拋出 LoginException 异常,执行 catch 语句块中的代码打印异常信息,如下所示。
用户名:
xiake8!
密码:
123456
用户名中包含有非数字的字符!

当用户输入的用户名长度不在 6~10 位时同样会拋出 LoginException 异常并打印异常信息,如下所示。
用户名:
administrator
密码:
123456
用户名长度必须在6~10位之间!

当用户输入的登录密码不等于 6 位时也会拋出 LogWException 异常,并打印出异常信息,如下所示。
用户名:
20181024
密码:
12345
密码长度必须为 6 位!

当用户输入的用户名和密码都符合要求时,则打印登录成功的信息,如下所示。
用户名:
20181024
密码:
123456
登录成功!

https://note.youdao.com/ynoteshare/index.html?id=3efb118a323c12c4f8e2f5bbb1007fb3&type=note&_time=1637568515503

十三、多线程

Java 给多线程编程提供了内置的支持。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。

1.进程

在这里插入图片描述

2.线程

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。

3. 创建

3.1 继承Thread类创建多线程

JDK中提供了一个线程类Thread,通过继承Thread类,并重写Thread类中的run()方法便可实现多线程。
在Thread类中,提供了一个start()方法用于启动新线程,线程启动后,系统会自动调用run()方法,如果子类重写了该方法便会执行子类中的方法。

public class ThreadDemo {
	public static void main(String[] args) {
		MyThread myThread=new MyThread();//实例化一个类,发现此类是一个线程类
		myThread.run();//运行此类的run()方法,发现run()方法是一个死循环
		while(true) { //
			System.out.println("这个输出语句是在main方法里面的");
		}
	}

}

class MyThread extends Thread{
	public void run() {
		while(true) {
			System.out.println("这个输出语句是在MyThread类的run方法里面的");
		}
	}
}
public class ThreadDemo {
	public static void main(String[] args) {
		MyThread myThread=new MyThread();//实例化一个类,发现此类是一个线程类
		myThread.start();//启动线程,此线程已经可以争取到CPU的控制权
		while(true) {
			System.out.println("这个输出语句是在main方法里面的");
		}
	}

}

class MyThread extends Thread{
	public void run() {
		while(true) {
			System.out.println("这个输出语句是在MyThread类的run方法里面的");
		}
	}
}

结果:
这个输出语句是在MyThread类的run方法里面的
这个输出语句是在main方法里面的

public class ThreadDemo {
	public static void main(String[] args) {
		MyThread myThread=new MyThread();//实例化一个类,发现此类是一个线程类
		myThread.start();
		while(true) {
			try {
				Thread.sleep(500);
			} catch(InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("这个输出语句是在main方法里面的");
		}
	}

}

class MyThread extends Thread{
	public void run() {
		while(true) {
			try {
				Thread.sleep(500);
			} catch(InterruptedException e) {
				e.printStackTrace();
			}
			
			System.out.println("这个输出语句是在MyThread类的run方法里面的");
		}
	}
}

结果:
这个输出语句是在main方法里面的
这个输出语句是在MyThread类的run方法里面的
这个输出语句是在main方法里面的
这个输出语句是在MyThread类的run方法里面的
这个输出语句是在main方法里面的
这个输出语句是在MyThread类的run方法里面的
这个输出语句是在main方法里面的
这个输出语句是在MyThread类的run方法里面的


public class text {
	public static void main(String[] args) {
		MyThread t1=new MyThread("第一个窗口");
		MyThread t2=new MyThread("第二个窗口");
		MyThread t3=new MyThread("第三个窗口");
		MyThread t4=new MyThread("第四个窗口");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}


class MyThread extends Thread{
	int ticketNum=100;
	String name;
	public MyThread() {
		
	}
	public MyThread(String n) {
		name=n;
	}
	public void run() {
		while(true) {
			if(ticketNum>0) {
				System.out.println(name+"正在发售"+ticketNum--+"张票");
			}
		}
	}
 }

3.2 实现Runnable接口创建多线程

通过继承Thread类实现了多线程,但是这种方式有一定的局限性。因为Java中只支持单继承,一个类一旦继承了某个父类就无法再继承Thread类。
•Thread类提供了另外一个构造方法Thread(Runnable target),其中Runnable是一个接口,它只有一个run()方法。
•当通过Thread(Runnable target))构造方法创建线程对象时,只需为该方法传递一个实现了Runnable接口的实例对象,这样创建的线程将调用实现了Runnable接口中的run()方法作为运行代码,而不需要调用Thread类中的run()方法

public class Example03 {
	public static void main(String[] args) {
		MyThread myThread = new MyThread();  // 创建MyThread的实例对象    
		Thread thread=new Thread(myThread);  // 创建线程对象
		thread.start();                          // 开启线程,执行线程中的run()方法
		while (true) {
			System.out.println("main()方法在运行");
		 }
	   }
}
class MyThread implements Runnable {
	public void run() {          // 线程的代码段,当调用start()方法时,线程从此处开始执行
		while (true) {
			System.out.println("MyThread类的run()方法在运行");
		}
	}
}

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread myThread=new MyThread("张三");
        MyThread myThread1=new MyThread("李四");
        Thread thread1 =new Thread(myThread);
        Thread thread2 =new Thread(myThread1);
        thread1.start();
        thread2.start();
    }
}
class MyThread implements Runnable{
    String nameinformation;
    public MyThread() {
    }
    public MyThread(String name) {
        nameinformation=name;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i=0; i<=100; i+=5) {
            System.out.println(nameinformation+"跑了5米");
        }
        System.out.println(nameinformation+"跑完了100米");
    }
}

请添加图片描述

public class ThreadDemo {
	public static void main(String[] args) {
	     MyThread myThread=new MyThread();
	     Thread thread=new Thread(myThread,"张三");
	     Thread thread1=new Thread(myThread,"李四");
	     
	     thread.start();
	     thread1.start();
	     Thread.currentThread().getName();
	     
  }
}

class MyThread implements Runnable{

	
	@Override
	public void run() {
		for(int i=0;i<20;i++){
			System.out.println(Thread.currentThread().getName()+"跑了5米");
			}
	    System.out.println(Thread.currentThread().getName()+"跑完100米");
		}
}

请添加图片描述
实现Runnable接口相对于继承Thread类来说,有如下显著好处:
(1)适合多个相同程序代码的线程去处理同一个资源的情况,把线程同程序代码、数据有效地分离,很好的体现出面向对象的编程思想
(2)可以避免由于Java单继承带来的局限性。
(3)大多数的应用程序都会采用实现Runnable的方式实现多线程的创建

4.一个线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
下图显示了一个线程完整的生命周期。
在这里插入图片描述
1、新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
2、就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
3、运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
4、阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
a、等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
b、同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
c、其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
5、死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

4.1 线程的状态转换

•在程序中,线程不同的状态是可以转换的

4.2 线程的优先级

优先级越高的线程获得CPU执行的机会越大,而优先级越低的线程获得CPU执行的机会越小。
•线程的优先级用1~10之间的整数来表示,数字越大优先级越高。
除了可以直接使用数字表示线程的优先级,还可以使用Thread类中提供的三个静态常量表示线程的优先级

Thread类的静态变量功能描述
static int MAX_PRIORITY表示线程的最高优先级,相当于值10
static int MIN_PRIORITY表示线程的最低优先级,相当于值1
static int NORM_PRIORIY表示线程的普通优先级,相当于值5

程序在运行期间,处于就绪状态的每个线程都有自己的优先级,例如main线程具有普通优先级。
•线程优先级不是固定不变的,可以通过Thread类的setPriority(int newPriority)方法对其进行设置,该方法中的参数newPriority接收的是1~10之间的整数或者Thread类的三个静态常量。

(1) 自定义一个类Demo,使其实现Runnable接口。
(2) 在Demo类中重写run()方法,在方法内编写一个for循环,循环体内打印:线程名称+循环次数。
(3) 编写测试类Example04,在Example04类的main()方法中,创建一个Demo对象,利用Thread的构造方法创建三个线程对象并命名,使用setPriority()方法将其中两个线程的优先级设为最大和最小,最后开启三个线程的start()方法。


public class text {
	public static void main(String[] args) {
		Test test=new Test();
		Thread thread1=new Thread(test,"第一个线程");
		Thread thread2=new Thread(test,"第二个线程");
		Thread thread3=new Thread(test,"第三个线程");
		thread3.setPriority(Thread.MAX_PRIORITY);
		thread1.setPriority(1);
		
		thread1.start();
		thread2.start();
		thread3.start();
		
		
	}
}

class Test implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<5;i++) {
			
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
			System.out.println(Thread.currentThread().getName()+"----------------------"+i);
		}
	}
	
}

1、在多线程程序中,可以通过设置线程的优先级别来控制线程获得CPU执行的几率。
2、线程的优先级用1~10之间的整数来表示,数字越大优先级越高。也可以使用静态常量表示线程的优先级,如:MAX_PRIORITY、MIN_PRIORITY、NORM_PRIORITY。
3、虽然Java中提供了10个线程优先级,但是这些优先级需要操作系统的支持,不同的操作系统对优先级的支持是不一样的,不能很好的和Java中线程优先级一一对应,因此,在设计多线程应用程序时,其功能的实现一定不能依赖于线程的优先级,而只能把线程优先级作为一种提高程序效率的手段。

4.3 线程休眠

如果希望人为地控制线程,使正在执行的线程暂停,将CPU让给别的线程。这时,可以使用静态方法sleep(long millis),该方法可以让当前正在执行的线程暂停一段时间,进入休眠等待状态。为了让初学者更好地掌握线程休眠,案例中将启动三个线程共同出售10张票来演示线程休眠及休眠引发的结果。
1、设计思路(实现原理)
(1) 自定义一个类Ticket,使其实现Runnable接口,并在该类中创建int类型私有属性tickets,赋初值为10。
(2) 在Ticket类中重写run()方法,在方法内编写一个while循环。循环体内判断ticket值,当大于0时,使用sleep(long millis)方法使线程休眠1秒钟,并打印:当前线程名称+“正在出售第”+循环次数;否则结束循环。每执行一次while循环,tickets值减一。
(3) 编写测试类Example05,在Example05类的main()方法中,创建一个Ticket对象,利用Thread的构造方法创建三个线程对象并命名,并开启三个线程的start()方法。

class Ticket implements Runnable {
    private int tickets = 10;
    public void run() {
        while (true) {
            if (tickets > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()
                + "正在出售第" + (tickets--) + "张票");
            }
        }
    }
}
public class Example05 {
    public static void main(String[] args) {
        Ticket tr = new Ticket();
        Thread t1 = new Thread(tr);
        Thread t2 = new Thread(tr);
        Thread t3 = new Thread(tr);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

4.4 线程让步

在校园中,我们经常会看到同学互相抢篮球,当某个同学抢到篮球后就可以拍一会,之后他会把篮球让出来,大家重新开始抢篮球,这个过程就相当于Java程序中的线程让步。在多线程程序中,可以通过线程的yield()方法将线程转换成就绪状态,让系统的调度器重新调度一次,达到线程让步的目的。案例中将在一个多线程程序中,通过yield()方法对其中一个线程设置线程让步来演示。
1、设计思路(实现原理)
(1) 自定义一个类Demo,使其实现Runnable接口。
(2) 在Demo类中覆写run()方法,在方法内编写一个for循环,循环体内,先执行线程让步的方法yield(),然后输出打印:线程名称+循环次数。
(3) 编写测试类Example06,在Example06类的main()方法中,创建一个Demo对象,利用Thread的构造方法创建两个线程对象,并执行线程对象的start()方法,同时编写for循环,循环内打印“main:”+当前循环次数。

class Demo implements Runnable{
    public void run(){
        for(int x = 0 ; x < 5 ; x++){
            Thread.yield();
            System.out.println(Thread.currentThread().getName()+"..."+x);
        }
    }
}
public class Example06 {
    public static void main(String[] args)throws Exception {
        Demo d = new Demo();
        Thread t0 = new Thread(d);
        Thread t1 = new Thread(d);
        t0.start();
        t1.start();
        for(int x = 0 ; x<5 ; x++){
            System.out.println("main..."+x);
        }
    }
}

4.5 线程插队

在火车站买票的时候,有的乘客着急赶火车,会插到队伍前面先买车票,其他乘客再买票。那么在多线程程序中,也可以通过线程插队,让插队的线程先执行完,然后本线程才开始执行。在案例中将通过使用join()方法来演示线程插队。
1、设计思路(实现原理)
(1) 自定义一个类Demo,使其实现Runnable接口。
(2) 在Demo类中覆写run()方法,在方法内编写一个for循环,循环体内打印:线程名称+循环次数。
(3) 编写测试类Example07,在Example07类的main()方法中,创建一个Demo对象,利用Thread的构造方法创建两个线程对象,分别命名“排队队员”和“插队队员”,然后编写两个线程对象的start()方法,然后调用“插队队员”线程的join()方法。


public class text {
	public static void main(String[] args) {
		Test test=new Test();
		Thread thread1=new Thread(test,"排队线程");
		Thread thread2=new Thread(test,"插队线程");
		
	
		
		thread1.start();
		thread2.start();
        
		try {
			thread2.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

class Test implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<50;i++) {
			
			System.out.println(Thread.currentThread().getName()+"----------------------"+i);
		}
	}
	
}

在这里插入图片描述

public class text {
	public static void main(String[] args) {
		Demo demo= new Demo();
		Thread t1=new Thread(demo,"张三");
		Thread t2=new Thread(demo,"李四");
		t1.start();
		t2.start();
	}
}

class Demo implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub

			System.out.println(Thread.currentThread().getName()+"进入洗手间,锁上洗手间的门");
			for(int i=0;i<10;i++) {
				System.out.println(Thread.currentThread().getName()+"正在洗手间");
		if(i==3) {
			System.out.println(Thread.currentThread().getName()+"使用完毕,开门");
			break; 
		}
		
	}
 }
	
}

4.6 同步代码块

生活中,会遇到两人上洗手间的问题,甲使用洗手间的时候会锁上门,乙看到门锁上了,就需要等甲使用完后再使用的。那么在多线程程序中,可以通过将共享资源放在同步代码块内来实现多个线程同步处理共享资源的问题。本案例将通过两个线程共享资源来演示同步代码块的使用。
1、设计思路(实现原理)
(1) 自定义一个类Demo,使其实现Runnable接口。
(2) 在Demo类中覆写run()方法,在方法内编写synchronized同步代码块,在进入同步代码块时,打印线程名称,然后编写一个for循环,循环体内打印:运行线程名称+循环次数。当循环次数等于3时,跳出循环。
(3) 编写测试类Example08,在Example08类的main()方法中,创建一个Demo对象,利用Thread的构造方法创建两个线程对象,分别命名“张三”和“李四”,执行两个线程的start()方法。

public class text {
	public static void main(String[] args) {
		Demo demo= new Demo();
		Thread t1=new Thread(demo,"张三");
		Thread t2=new Thread(demo,"李四");
		t1.start();
		t2.start();
	}
}

class Demo implements Runnable{
//class Demo implements Runnable{
	//private String suo="aaaaaa";
	 //synchronized(suo) 

	@Override
	public void run() {
		// TODO Auto-generated method stub
        synchronized(this) {  
		//给这一段语句块加锁
			System.out.println(Thread.currentThread().getName()+"进入洗手间,锁上洗手间的门");
			for(int i=0;i<10;i++) {
				System.out.println(Thread.currentThread().getName()+"正在洗手间");
		if(i==3) {
			System.out.println(Thread.currentThread().getName()+"使用完毕,开门");
			break; 
		}
	 }
	}
	}
}

结果:
张三进入洗手间,锁上洗手间的门
张三正在洗手间
张三正在洗手间
张三正在洗手间
张三正在洗手间
张三使用完毕,开门
李四进入洗手间,锁上洗手间的门
李四正在洗手间
李四正在洗手间
李四正在洗手间
李四正在洗手间
李四使用完毕,开门

4.7 多线程死锁

1、什么是死锁
当一个线程拥有A对象锁,并等待B对象锁的时候,另一个线程拥有B对象锁,并等待A对象锁,这就造成了死锁。
一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,也可能会造成死锁。
案例介绍:
假如宿舍只剩一双鞋,这时候小明和小朋两个人都要出去,两个人一个拿到了左脚鞋子,一个人拿到了右脚鞋子,可是谁也不让给谁,于是乎就造成了死锁现象,谁也别出去了。

package com.nxw.Thread.dieLock;

public class DieLock{

    public static void main(String[] args) {
        Runnable xiaoming = new Runnable() {
            public void run() {
                synchronized (Shoes.left){
                    System.out.println("小明拿到了左脚鞋子");
                    synchronized (Shoes.right){
                        System.out.println("小明拿到了右脚鞋子,他可以穿鞋子走人了。。。");
                    }
                }
            }
        };

        Runnable xiaopeng = new Runnable() {
            public void run() {
                synchronized (Shoes.right){
                    System.out.println("小朋拿到了右脚鞋子");
                    synchronized (Shoes.left){
                        System.out.println("小朋拿到了左脚鞋子,他可以穿鞋子走人了。。。");
                    }
                }
            }
        };

        Thread thread1 = new Thread(xiaoming,"小明");
        Thread thread2 = new Thread(xiaopeng,"小朋");

        thread1.start();
        thread2.start();
    }


}

class Shoes {
    public static String left = "leftShoes";
    public static String right = "rightShoes";
}


结果为
小明拿到了左脚鞋子
小朋拿到了右脚鞋子

https://www.cnblogs.com/zhrb/p/6872265.html

十四、I/O处理

1.文件

1、什么是路径,什么是相对路径和绝对路径
答:路径表示文件或目录在windows或Linux系统的位置
相对路径是当前目录下找到的路径,相对于当前的位置,以填写目录文件为参考,使用“…/”或"./“指向上一级 或 使用”…/…/“指向上上一级叫相对路径。
绝对路径是根目录下找到的路径,通常我们直接使用”/"代表从根目录开始的目录路径,这个叫绝对路径。(文件在硬盘上真正存在的路径)
绝对路径: 凡是以根开始的路径就是绝对路径
相对路径: 不是以根为开头的路径就是相对路径
2、文件夹和文件是什么关系
答:同一级关系
3.如果你是一个Java程序,你可能涉及到哪些文件操作。
答:增删改查
4.常见的文件(夹)属性有哪些
答:类型、位置、大小、占用空间、包含、创建时间、只读、隐藏、存档

Java文件类以抽象的方式代表文件名和目录路径名。该类主要用于文件和目录的创建、文件的查找和文件的删除等。
File对象代表磁盘中实际存在的文件和目录。通过以下构造方法创建一个File对象。
通过给定的父抽象路径名和子路径名字符串创建一个新的File实例。

File(File parent, String child);

通过将给定路径名字符串转换成抽象路径名来创建一个新 File 实例。

File(String pathname) 

根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

File(String parent, String child) 

通过将给定的 file: URI 转换成一个抽象路径名来创建一个新的 File 实例。

File(URI uri) 

创建File对象成功后,可以使用以下列表中的方法操作文件。
1、public String getName()返回由此抽象路径名表示的文件或目录的名称。
2、public String getParent()、 返回此抽象路径名的父路径名的路径名字符串,如果此路径名没有指定父目录,则返回 null。
3、public File getParentFile()返回此抽象路径名的父路径名的抽象路径名,如果此路径名没有指定父目录,则返回 null。
4、public String getPath()将此抽象路径名转换为一个路径名字符串。
5、public boolean isAbsolute()测试此抽象路径名是否为绝对路径名。
6、public String getAbsolutePath()返回抽象路径名的绝对路径名字符串。
7、public boolean canRead()测试应用程序是否可以读取此抽象路径名表示的文件。
8、public boolean canWrite()测试应用程序是否可以修改此抽象路径名表示的文件。
9、public boolean exists()测试此抽象路径名表示的文件或目录是否存在。
10、public boolean isDirectory()测试此抽象路径名表示的文件是否是一个目录。
11、public boolean isFile()测试此抽象路径名表示的文件是否是一个标准文件。
12、public long lastModified()返回此抽象路径名表示的文件最后一次被修改的时间。
13、public long length()返回由此抽象路径名表示的文件的长度。
14、public boolean createNewFile() throws IOException当且仅当不存在具有此抽象路径名指定的名称的文件时,原子地创建由此抽象路径名指定的一个新的空文件。
15、public boolean delete() 删除此抽象路径名表示的文件或目录。
16、public void deleteOnExit()在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。
17、public String[] list()返回由此抽象路径名所表示的目录中的文件和目录的名称所组成字符串数组。18、public String[] list(FilenameFilter filter)返回由包含在目录中的文件和目录的名称所组成的字符串数组,这一目录是通过满足指定过滤器的抽象路径名来表示的。
19、public File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名所表示目录中的文件。
20、public File[] listFiles(FileFilter filter)返回表示此抽象路径名所表示目录中的文件和目录的抽象路径名数组,这些路径名满足特定过滤器。
21、public boolean mkdir()创建此抽象路径名指定的目录。
22、public boolean mkdirs()创建此抽象路径名指定的目录,包括创建必需但不存在的父目录。
23、public boolean renameTo(File dest) 重新命名此抽象路径名表示的文件。
24、public boolean setLastModified(long time)设置由此抽象路径名所指定的文件或目录的最后一次修改时间。
25、public boolean setReadOnly()标记此抽象路径名指定的文件或目录,以便只可对其进行读操作。26、public static File createTempFile(String prefix, String suffix, File directory) throws IOException在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。
27、public static File createTempFile(String prefix, String suffix) throws IOException在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称。
28、public int compareTo(File pathname)按字母顺序比较两个抽象路径名。
29、public int compareTo(Object o)按字母顺序比较抽象路径名与给定对象。
30、public boolean equals(Object obj)测试此抽象路径名与给定对象是否相等。
31、public String toString() 返回此抽象路径名的路径名字符串。

import java.io.File;
import java.sql.Date;

public class DemoTest {
public static void main(String[] args) {
	String path="C:/windows/";
	File f=new File(path,"notepad.exe");
	System.out.println("记事本文件的信息如下:");
	System.out.println("================分割线=========");
	System.out.println("文件长度:"+f.length()+"字节");
	System.out.println("文件或者目录:"+(f.isFile()?"是文件":"不是文件"));
	System.out.println("文件或者目录:" + (f.isDirectory() ? "是目录" : "不是目录"));
	System.out.println("是否可读:"+(f.canRead()?"可读取":"不可读取"));
    System.out.println("是否可写:" + (f.canWrite() ? "可写入" : "不可写入"));
    System.out.println("是否隐藏:" + (f.isHidden() ? "是隐藏文件" : "不是隐藏文件"));
    System.out.println("最后修改日期:" + new Date(f.lastModified()));
    System.out.println("文件名称:" + f.getName());
    System.out.println("文件路径:" + f.getPath());
    System.out.println("绝对路径:" + f.getAbsolutePath());
	
	
}
}

结果:
记事本文件的信息如下:
========分割线=
文件长度:181248字节
文件或者目录:是文件
文件或者目录:不是目录
是否可读:可读取
是否可写:可写入
是否隐藏:不是隐藏文件
最后修改日期:2019-03-19
文件名称:notepad.exe
文件路径:C:\windows\notepad.exe
绝对路径:C:\windows\notepad.exe

import java.io.File;


public class DemoTest {
public static void main(String[] args) {
	String dirname="/java";
	File f1=new File(dirname);
	if(f1.isDirectory()) {
		System.out.println("是目录");
	}else System.out.println("不是目录");
}
}

不存在

import java.io.File;


public class DemoTest {
public static void main(String[] args) {
	String dirname="./";
	File f1=new File(dirname);
	if(f1.isDirectory()) {
		System.out.println("是目录");
	}else System.out.println("不是目录");
}
}

是目录

import java.io.File;


public class DemoTest {
public static void main(String[] args) {
	String dirname="./";
	//String dirname="../";//文件的附目录
	//String dirname="../../";
	File f1=new File(dirname);
	if(f1.isDirectory()) {
		System.out.println("目录:"+dirname);
		String[] s=f1.list();
		for(int i =0;i<s.length;i++) {
			File f=new File(dirname+"/"+s[i]);
			if(f.isDirectory()) {
				System.out.println(s[i]+"是目录");
			}else System.out.println(s[i]+"是文件");
		}
	}else System.out.println("不是目录");
}
}

结果:
目录:./
.classpath是文件
.project是文件
.settings是目录
bin是目录
src是目录

文件的附目录
目录:…/
.metadata是目录
.recommenders是目录
Demo是目录
RemoteSystemsTempFiles是目录
Servers是目录
Test是目录

1.1. 创建和删除文件

File 类不仅可以获取已知文件的属性信息,还可以在指定路径创建文件,以及删除一个文件。创建文件需要调用 createNewFile() 方法,删除文件需要调用 delete() 方法。无论是创建还是删除文件通常都先调用 exists() 方法判断文件是否存在。
例 2
假设要在 C 盘上创建一个 test.txt 文件,程序启动时会检测该文件是否存在,如果不存在则创建;如果存在则删除它再创建。

import java.io.File;
import java.io.IOException;


public class DemoTest {
public static void main(String[] args) throws IOException {
	File f=new File("C:\\tmp\\test.txt");// '\\'='\'
	//File f=new File("C:"+File.separator+"tmp"+File.separator+"test.txt");
	//这两者的功能一样,但是后者移植性要好些
	if(f.exists()) {
		f.delete();
	}
	f.createNewFile();
}
}

运行程序之后可以发现,在 C 盘中已经创建好了 test.txt 文件。但是如果在不同的操作系统中,路径的分隔符是不一样的,例如:
Windows 中使用反斜杠\表示目录的分隔符。
Linux 中使用正斜杠/表示目录的分隔符。

那么既然 Java 程序本身具有可移植性的特点,则在编写路径时最好可以根据程序所在的操作系统自动使用符合本地操作系统要求的分隔符,这样才能达到可移植性的目的。要实现这样的功能,则就需要使用 File 类中提供的两个常量。
代码修改如下:

public static void main(String[] args) throws IOException {
    String path = "C:" + File.separator + "test.txt"; // 拼凑出可以适应操作系统的路径
    File f = new File(path);
    if (f.exists()) // 判断文件是否存在
    {
        f.delete(); // 存在则先删除
    }
    f.createNewFile(); // 再创建
}

程序的运行结果和前面程序一样,但是此时的程序可以在任意的操作系统中使用。

注意:在操作文件时一定要使用 File.separator 表示分隔符。在程序的开发中,往往会使用 Windows 开发环境,因为在 Windows 操作系统中支持的开发工具较多,使用方便,而在程序发布时往往是直接在 Linux 或其它操作系统上部署,所以这时如果不使用 File.separator,则程序运行就有可能存在问题。关于这一点我们在以后的开发中一定要有所警惕。

1.2. 创建和删除目录

File 类除了对文件的创建和删除外,还可以创建和删除目录。创建目录需要调用 mkdir() 方法,删除目录需要调用 delete() 方法。无论是创建还是删除目录都可以调用 exists() 方法判断目录是否存在。
例 3
编写一个程序判断 C 盘根目录下是否存在 config 目录,如果存在则先删除再创建。实现代码如下:

public class Test04 {
    public static void main(String[] args) {
        String path = "C:/config/"; // 指定目录位置
        File f = new File(path); // 创建File对象
        if (f.exists()) {
            f.delete();
        }
        f.mkdir(); // 创建目录
    }
}

1、什么是流,为什么需要流,什么时候需要流?
 流是个抽象的概念,是对输入输出设备的抽象,Java程序中,对于数据的输入/输出操作都是以“流”的方式进行。设备可以是文件,网络,内存等。
2、流的种类,怎么区分
1. 处理的数据单位不同,可分为:字符流,字节流
2.数据流方向不同,可分为:输入流,输出流
3.功能不同,可分为:节点流,处理流
3、字节是什么概念,字节输入流、输出流怎么理解

字节输入流,是将你输入的文件,以字节(字节是以010101…方式表示)的方式读入并存放在内存中,等待读出。字节输出流很显然就是以字节为单位从程序文件中输出内容
从物理层面来看,流中数据都是二进制比特流。而计算机中储存信息的基本单位是字节(Byte)。因此,可以认为计算机中信息传输在底层是靠字节流来实现的。字符流只是通过不同的字符编码方式,对字节流的封装,即字符流的实现还是得依靠字节流。

综上观点,总结字节流与字符流如下所示:

(a)字节流默认是不带缓冲区的,而字符流默认是带缓冲区的。

(b)字节流是底层数据流,是数据有意义的最小单位。字符流是字节流的包装,底层实现是字节流。

(c)基于b点,文本文件可以用字节流来实现,当然使用字符流速度会更快。

判断当前流是输入流还是输出流的依据是二进制数据相对于计算机内存的位置,输入流是输入计算机内存是二进制数据,输出流是从计算机内存输出的二进制数据。而计算机程序在运行期间会储存在到计算机内存中,因此总的来说就是数据的来源、取向是相对程序而言的。比如键盘键入数据属于输入流,内存数据持久化到磁盘中属于输出流。

十五、流

1.什么是输入/输出流

1、Java 程序通过流来完成输入/输出,所有的输入/输出以流的形式处理。因此要了解 I/O 系统,首先要理解输入/输出流的概念。

2、输入就是将数据从各种输入设备(包括文件、键盘等)中读取到内存中,输出则正好相反,是将数据写入到各种输出设备(比如文件、显示器、磁盘等)。例如键盘就是一个标准的输入设备,而显示器就是一个标准的输出设备,但是文件既可以作为输入设备,又可以作为输出设备。

3、数据流是 Java 进行 I/O 操作的对象,它按照不同的标准可以分为不同的类别。
a、按照流的方向主要分为输入流和输出流两大类。
b 、数据流按照数据单位的不同分为字节流和字符流。
c、按照功能可以划分为节点流和处理流。

数据流的处理只能按照数据序列的顺序来进行,即前一个数据处理完之后才能处理后一个数据。数据流以输入流的形式被程序获取,再以输出流的形式将数据输出到其它设备。图 1 为输入流模式,图 2 为输出流模式。
在这里插入图片描述
在这里插入图片描述

1.2.输入流

Java 流相关的类都封装在 java.io 包中,而且每个数据流都是一个对象。所有输入流类都是 InputStream 抽象类(字节输入流)和 Reader 抽象类(字符输入流)的子类。其中 InputStream 类是字节输入流的抽象类,是所有字节输入流的父类,其层次结构如图 3 所示。
在这里插入图片描述
lnputStream 类中所有方法遇到错误时都会引发 IOException 异常。如下是该类中包含的常用方法。
InputStream 类常用方法
1、int read() 从输入流读入一个 8 字节的数据,将它转换成一个 0~ 255 的整数,返回一个整数,如果遇到输入流的结尾返回 -1
2、int read(byte[] b) 从输入流读取若干字节的数据保存到参数 b 指定的字节数组中,返回的字节数表示读取的字节数,如果遇到输入流的结尾返回 -1
3、int read(byte[] b,int off,int len) 从输入流读取若干字节的数据保存到参数 b 指定的字节数组中,其中 off 是指在数组中开始保存数据位置的起始下标,len 是指读取字节的位数。返回的是实际读取的字节数,如果遇到输入流的结尾则返回 -1
4、void close() 关闭数据流,当完成对数据流的操作之后需要关闭数据流
int available() 返回可以从数据源读取的数据流的位数。
5、int available()返回可以从输入流中读取的字节数
6、long skip(long n)从输入流中跳过参数 n 指定数目的字节。该方法返回跳过的字节数7、void mark(int readLimit)在输入流的当前位置开始设置标记,参数 readLimit 则指定了最多被设置标记的字节数
8、boolean markSupported()判断当前输入流是否允许设置标记,是则返回 true,否则返回 false
9、void reset()将输入流的指针返回到设置标记的起始处
注意:在使用 mark() 方法和 reset() 方法之前,需要判断该文件系统是否支持这两个方法,以避免对程序造成影响。

1. 3. 字节输出流

OutputStream 类及其子类的对象表示一个字节输出流。OutputStream 类的常用子类如下。
1、ByteArrayOutputStream 类:向内存缓冲区的字节数组中写数据。
2、FileOutputStream 类:向文件中写数据。
3、PipedOutputStream 类:连接到一个 4、PipedlntputStream(管道输入流)。
5、ObjectOutputStream 类:将对象序列化。

利用 OutputStream 类的方法可以从流中写入一个或一批字节。表 2 列出了 OutputStream 类的常用方法。

方法名及返回值类型及说明
1、void write(int b)向输出流写入一个字节。这里的参数是 int 类型,但是它允许使用表达式,而不用强制转换成 byte 类型。为了提高 I/O 操作的效率,建议尽量使用write() 方法的另外两种形式
2、void write(byte[] b)把参数 b 指定的字节数组中的所有字节写到输出流中
void write(byte[] b,int off,int len)把参数 b 指定的字节数组中的若干字节写到输出流中。其中,off 指定字节数组中的起始下标,len 表示元素个数
3、void close()关闭输出流。写操作完成后,应该关闭输出流。系统将会释放与这个输出流相关的资源。注意,OutputStream 类本身的 close() 方法不执行任何操作,但是它的许多子类重写了 close() 方法
4、void flush()为了提高效率,在向输出流中写入数据时,数据一般会先保存到内存缓冲区中,只有当缓冲区中的数据达到一定程度时,缓冲区中的数据才会被写入输出流中。使用 flush() 方法则可以强制将缓冲区中的数据写入输出流,并清空缓冲区

1. 4. 字节数组输入流

ByteArrayInputStream 类可以从内存的字节数组中读取数据,该类有如下两种构造方法重载形式。
1、ByteArrayInputStream(byte[] buf):创建一个字节数组输入流,字节数组类型的数据源由参数 buf 指定。
2、ByteArrayInputStream(byte[] buf,int offse,int length):创建一个字节数组输入流,其中,参数 buf 指定字节数组类型的数据源,offset 指定在数组中开始读取数据的起始下标位置,length 指定读取的元素个数。
例 1
使用 ByteArrayInputStream 类编写一个案例,实现从一个字节数组中读取数据,再转换为 int 型进行输出。代码如下:

public class test08 {
    public static void main(String[] args) {
        byte[] b = new byte[] { 1, -1, 25, -22, -5, 23 }; // 创建数组
        ByteArrayInputStream bais = new ByteArrayInputStream(b, 0, 6); // 创建字节数组输入流
        int i = bais.read(); // 从输入流中读取下一个字节,并转换成int型数据
        while (i != -1) { // 如果不返回-1,则表示没有到输入流的末尾
            System.out.println("原值=" + (byte) i + "\t\t\t转换为int类型=" + i);
            i = bais.read(); // 读取下一个
        }
    }
}

在该示例中,字节输入流 bais 从字节数组 b 的第一个元素开始读取 4 字节元素,并将这 4 字节转换为 int 类型数据,最后返回。

提示:上述示例中除了打印 i 的值外,还打印出了 (byte)i 的值,由于 i 的值是从 byte 类型的数据转换过来的,所以使用 (byte)i 可以获取原来的 byte 数据。

该程序的运行结果如下:
原值=1 转换为int类型=1
原值=-1 转换为int类型=255
原值=25 转换为int类型=25
原值=-22 转换为int类型=234
原值=-5 转换为int类型=251
原值=23 转换为int类型=23

1.5. 字节数组输出流

ByteArrayOutputStream 类可以向内存的字节数组中写入数据,该类的构造方法有如下两种重载形式。
1、ByteArrayOutputStream():创建一个字节数组输出流,输出流缓冲区的初始容量大小为 32 字节。
2、ByteArrayOutputStream(int size):创建一个字节数组输出流,输出流缓冲区的初始容量大小由参数 size 指定。

ByteArrayOutputStream 类中除了有前面介绍的字节输出流中的常用方法以外,还有如下两个方法。
1、intsize():返回缓冲区中的当前字节数。
2、byte[] toByteArray():以字节数组的形式返回输出流中的当前内容。
例 2
使用 ByteArrayOutputStream 类编写一个案例,实现将字节数组中的数据输出,代码如下所示。

public class Test09 {
    public static void main(String[] args) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] b = new byte[] { 1, -1, 25, -22, -5, 23 }; // 创建数组
        baos.write(b, 0, 6); // 将字节数组b中的前4个字节元素写到输出流中
        System.out.println("数组中一共包含:" + baos.size() + "字节"); // 输出缓冲区中的字节数
        byte[] newByteArray = baos.toByteArray(); // 将输出流中的当前内容转换成字节数组
        System.out.println(Arrays.toString(newByteArray)); // 输出数组中的内容
    }
}

该程序的输出结果如下:
数组中一共包含:6字节
[1, -1, 25, -22, -5, 23]

1.6. 文件输入流

FileInputStream 是 Java 流中比较常用的一种,它表示从文件系统的某个文件中获取输入字节。通过使用 FileInputStream 可以访问文件中的一个字节、一批字节或整个文件。

在创建 FileInputStream 类的对象时,如果找不到指定的文件将拋出 FileNotFoundException 异常,该异常必须捕获或声明拋出。

FileInputStream 常用的构造方法主要有如下两种重载形式。
1、FileInputStream(File file):通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
2、FileInputStream(String name):通过打开一个到实际文件的链接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

下面的示例演示了 FileInputStream() 两个构造方法的使用。

try {
    // 以File对象作为参数创建FileInputStream对象
    FileInputStream fis1 = new FileInputStream(new File("F:/mxl.txt"));
    // 以字符串值作为参数创建FilelnputStream对象
    FileInputStream fis2 = new FileInputStream("F:/mxl.txt");
} catch(FileNotFoundException e) {
    System.out.println("指定的文件找不到!");
}

例 3
假设有一个 D:\myJava\HelloJava.java 文件,下面使用 FileInputStream 类读取并输出该文件的内容。具体代码如下:

public class Test10 {
    public static void main(String[] args) {
        File f = new File("D:/myJava/HelloJava.java");
        FileInputStream fis = null;
        try {
            // 因为File没有读写的能力,所以需要有个InputStream
            fis = new FileInputStream(f);
            // 定义一个字节数组
            byte[] bytes = new byte[1024];
            int n = 0; // 得到实际读取到的字节数
            System.out.println("D:\\myJava\\HelloJava.java文件内容如下:");
            // 循环读取
            while ((n = fis.read(bytes)) != -1) {
                String s = new String(bytes, 0, n); // 将数组中从下标0到n的内容给s
                System.out.println(s);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

如上述代码,在 FileInputDemo 类的 main() 方法中首先创建了一个 File 对象 f,该对象指向 D:\myJava\HelloJava.java 文件。接着使用 FileInputStream 类的构造方法创建了一个 FileInputStream 对象 fis,并声明一个长度为 1024 的 byte 类型的数组,然后使用 FileInputStream 类中的 read() 方法将 HelloJava.java 文件中的数据读取到字节数组 bytes 中,并输出该数据。最后在 finally 语句中关闭 FileInputStream 输入流。

1.7. 文件输出流

FileOutputStream 类继承自 OutputStream 类,重写和实现了父类中的所有方法。FileOutputStream 类的对象表示一个文件字节输出流,可以向流中写入一个字节或一批字节。在创建 FileOutputStream 类的对象时,如果指定的文件不存在,则创建一个新文件;如果文件已存在,则清除原文件的内容重新写入。

FileOutputStream 类的构造方法主要有如下 4 种重载形式。
1、FileOutputStream(File file):创建一个文件输出流,参数 file 指定目标文件。
2、FileOutputStream(File file,boolean append):创建一个文件输出流,参数 file 指定目标文件,append 指定是否将数据添加到目标文件的内容末尾,如果为 true,则在末尾添加;如果为 false,则覆盖原有内容;其默认值为 false。
3、FileOutputStream(String name):创建一个文件输出流,参数 name 指定目标文件的文件路径信息。
4、FileOutputStream(String name,boolean append):创建一个文件输出流,参数 name 和 append 的含义同上。

注意:使用构造方法 FileOutputStream(String name,boolean append) 创建一个文件输出流对象,它将数据附加在现有文件的末尾。该字符串 name 指明了原文件,如果只是为了附加数据而不是重写任何已有的数据,布尔类型参数 append 的值应为 true。

对文件输出流有如下四点说明:
在 FileOutputStream 类的构造方法中指定目标文件时,目标文件可以不存在。
目标文件的名称可以是任意的,例如 D:\abc、D:\abc.de 和 D:\abc.de.fg 等都可以,可以使用记事本等工具打开并浏览这些文件中的内容。
目标文件所在目录必须存在,否则会拋出 java.io.FileNotFoundException 异常。
目标文件的名称不能是已存在的目录。例如 D 盘下已存在 Java 文件夹,那么就不能使用 Java 作为文件名,即不能使用 D:\Java,否则抛出 java.io.FileNotFoundException 异常。
例 4
同样是读取 D:\myJava\HelloJava.java 文件的内容,在这里使用 FileInputStream 类实现,然后再将内容写入新的文件 D:\myJava\HelloJava.txt 中。具体的代码如下:

public class Test11 {
    public static void main(String[] args) {
        FileInputStream fis = null; // 声明FileInputStream对象fis
        FileOutputStream fos = null; // 声明FileOutputStream对象fos
        try {
            File srcFile = new File("D:/myJava/HelloJava.java");
            fis = new FileInputStream(srcFile); // 实例化FileInputStream对象
            File targetFile = new File("D:/myJava/HelloJava.txt"); // 创建目标文件对象,该文件不存在
            fos = new FileOutputStream(targetFile); // 实例化FileOutputStream对象
            byte[] bytes = new byte[1024]; // 每次读取1024字节
            int i = fis.read(bytes);
            while (i != -1) {
                fos.write(bytes, 0, i); // 向D:\HelloJava.txt文件中写入内容
                i = fis.read(bytes);
            }
            System.out.println("写入结束!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close(); // 关闭FileInputStream对象
                fos.close(); // 关闭FileOutputStream对象
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

如上述代码,将 D:\myJava\HelloJava.java 文件中的内容通过文件输入/输出流写入到了 D:\myJava\HelloJava.txt 文件中。由于 HelloJava.txt 文件并不存在,所以在执行程序时将新建此文件,并写入相应内容。

2.字符流

尽管 Java 中字节流的功能十分强大,几乎可以直接或间接地处理任何类型的输入/输出操作,但利用它却不能直接操作 16 位的 Unicode 字符。这就要用到字符流。本节将重点介绍字符流的操作。

2. 1.字符输入流

Reader 类是所有字符流输入类的父类,该类定义了许多方法,这些方法对所有子类都是有效的。

Reader 类的常用子类如下。
CharArrayReader 类:将字符数组转换为字符输入流,从中读取字符。
StringReader 类:将字符串转换为字符输入流,从中读取字符。
BufferedReader 类:为其他字符输入流提供读缓冲区。
PipedReader 类:连接到一个 PipedWriter。
InputStreamReader 类:将字节输入流转换为字符输入流,可以指定字符编码。

与 InputStream 类相同,在 Reader 类中也包含 close()、mark()、skip() 和 reset() 等方法,这些方法可以参考 InputStream 类的方法。下面主要介绍 Reader 类中的 read() 方法,如表 1 所示。
方法名及返回值类型说明
int read()从输入流中读取一个字符,并把它转换为 0~65535 的整数。如果返回 -1, 则表示已经到了输入流的末尾。为了提高 I/O 操作的效率,建议尽量使用下面两种 read()方法
int read(char[] cbuf)从输入流中读取若干个字符,并把它们保存到参数 cbuf 指定的字符数组中。 该方法返回读取的字符数,如果返回 -1,则表示已经到了输入流的末尾
int read(char[] cbuf,int off,int len)从输入流中读取若干个字符,并把它们保存到参数 cbuf 指定的字符数组中。其中,off 指定在字符数组中开始保存数据的起始下标,len 指定读取的字符数。该方法返回实际读取的字符数,如果返回 -1,则表示已经到了输入流的末尾

2.2. 字符输出流

与 Reader 类相反,Writer 类是所有字符输出流的父类,该类中有许多方法,这些方法对继承该类的所有子类都是有效的。

Writer 类的常用子类如下。
CharArrayWriter 类:向内存缓冲区的字符数组写数据。
StringWriter 类:向内存缓冲区的字符串(StringBuffer)写数据。
BufferedWriter 类:为其他字符输出流提供写缓冲区。
PipedWriter 类:连接到一个 PipedReader。
OutputStreamReader 类:将字节输出流转换为字符输出流,可以指定字符编码。

与 OutputStream 类相同,Writer 类也包含 close()、flush() 等方法,这些方法可以参考 OutputStream 类的方法。下面主要介绍 Writer 类中的 write() 方法和 append() 方法,如表 2 所示。
方法名及返回值类型说明
void write(int c)向输出流中写入一个字符
void write(char[] cbuf)把参数 cbuf 指定的字符数组中的所有字符写到输出流中void write(char[] cbuf,int off,int len)把参数 cbuf 指定的字符数组中的若干字符写到输出流中。其中,off 指定字符数组中的起始下标,len 表示元素个数
void write(String str)向输出流中写入一个字符串
void write(String str, int off,int len)向输出流中写入一个字符串中的部分字符。其中,off 指定字符串中的起始偏移量,len 表示字符个数append(char c)将参数 c 指定的字符添加到输出流中
append(charSequence esq)将参数 esq 指定的字符序列添加到输出流中append(charSequence esq,int start,int end)将参数 esq 指定的字符序列的子序列添加到输出流中。其中,start 指定子序列的第一个字符的索引,end 指定子序列中最后一个字符后面的字符的索引,也就是说子序列的内容包含 start 索引处的字符,但不包括 end索引处的字符

注意:Writer 类所有的方法在出错的情况下都会引发 IOException 异常。关闭一个流后,再对其进行任何操作都会产生错误。

2.3. 字符文件输入流

为了读取方便,Java 提供了用来读取字符文件的便捷类——FileReader。该类的构造方法有如下两种重载形式。
FileReader(File file):在给定要读取数据的文件的情况下创建一个新的 FileReader 对象。其中,file 表示要从中读取数据的文件。
FileReader(String fileName):在给定从中读取数据的文件名的情况下创建一个新 FileReader 对象。其中,fileName 表示要从中读取数据的文件的名称,表示的是一个文件的完整路径。

在用该类的构造方法创建 FileReader 读取对象时,默认的字符编码及字节缓冲区大小都是由系统设定的。要自己指定这些值,可以在 FilelnputStream 上构造一个 InputStreamReader。

注意:在创建 FileReader 对象时可能会引发一个 FileNotFoundException 异常,因此需要使用 try catch 语句捕获该异常。

字符流和字节流的操作步骤相同,都是首先创建输入流或输出流对象,即建立连接管道,建立完成后进行读或写操作,最后关闭输入/输出流通道。
例 1
要将 D:\myJava\HelloJava.java 文件中的内容读取并输出到控制台,使用 FileReader 类的实现代码如下:

public class Test12 {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("D:/myJava/HelloJava.java"); // 创建FileReader对象
            int i = 0;
            System.out.println("D:\\myJava\\HelloJava.java文件内容如下:");
            while ((i = fr.read()) != -1) { // 循环读取
                System.out.print((char) i); // 将读取的内容强制转换为char类型
            }
        } catch (Exception e) {
            System.out.print(e);
        } finally {
            try {
                fr.close(); // 关闭对象
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

如上述代码,首先创建了 FileReader 字符输入流对象 fr,该对象指向 D:\myJava\HelloJava.java 文件,然后定义变量 i 来接收调用 read() 方法的返回值,即读取的字符。在 while 循环中,每次读取一个字符赋给整型变量 i,直到读取到文件末尾时退出循环(当输入流读取到文件末尾时,会返回值 -1)。

2.4. 字符文件输出流

Java 提供了写入字符文件的便捷类——FileWriter,该类的构造方法有如下 4 种重载形式。
FileWriter(File file):在指定 File 对象的情况下构造一个 FileWriter 对象。其中,file 表示要写入数据的 File 对象。
FileWriter(File file,boolean append):在指定 File 对象的情况下构造一个 FileWriter 对象,如果 append 的值为 true,则将字节写入文件末尾,而不是写入文件开始处。
FileWriter(String fileName):在指定文件名的情况下构造一个 FileWriter 对象。其中,fileName 表示要写入字符的文件名,表示的是完整路径。
FileWriter(String fileName,boolean append):在指定文件名以及要写入文件的位置的情况下构造 FileWriter 对象。其中,append 是一个 boolean 值,如果为 true,则将数据写入文件末尾,而不是文件开始处。

在创建 FileWriter 对象时,默认字符编码和默认字节缓冲区大小都是由系统设定的。要自己指定这些值,可以在 FileOutputStream 上构造一个 OutputStreamWriter 对象。

FileWriter 类的创建不依赖于文件存在与否,如果关联文件不存在,则会自动生成一个新的文件。在创建文件之前,FileWriter 将在创建对象时打开它作为输出。如果试图打开一个只读文件,将引发一个 IOException 异常。

注意:在创建 FileWriter 对象时可能会引发 IOException 或 SecurityException 异常,因此需要使用 try catch 语句捕获该异常。
例 2
编写一个程序,将用户输入的 4 个字符串保存到 D:\myJava\book.txt 文件中。在这里使用 FileWriter 类中的 write() 方法循环向指定文件中写入数据,实现代码如下:

public class Test13 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        FileWriter fw = null;
        try {
            fw = new FileWriter("D:\\myJava\\book.txt"); // 创建FileWriter对象
            for (int i = 0; i < 4; i++) {
                System.out.println("请输入第" + (i + 1) + "个字符串:");
                String name = input.next(); // 读取输入的名称
                fw.write(name + "\r\n"); // 循环写入文件
            }
            System.out.println("录入完成!");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            try {
                fw.close(); // 关闭对象
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

如上述代码,首先创建了一个指向 D:\myJava\book.txt 文件的字符文件输出流对象 fw,然后使用 for 循环录入 4 个字符串,并调用 write() 方法将字符串写入到指定的文件中。最后在 finally 语句中关闭字符文件输出流。

运行该程序,根据提示输入 4 个字符串,如下所示。接着打开 D:\myJava\book.txt 文件,将看到写入的内容,如图 1 所示。
请输入第1个字符串:
热点要闻
请输入第2个字符串:
个性推荐
请输入第3个字符串:
热搜新闻词
请输入第4个字符串:
本地看点
录入完成!

import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;

public class DemoIO1 {
public static void main(String[] args) {
	Scanner scanner=new Scanner(System.in);
	FileWriter fileWriter=null;
	try {
		fileWriter=new FileWriter("C:\\tmp\\javaText.txt");
		for(int i =0;i<4;i++){
			System.out.println("请输入第"+(i+1)+"个字符串:");
			String str=scanner.next();
			fileWriter.write(str+"\r\n");
		}
		System.out.println("录入完成");
	} catch (Exception e) {
		// TODO: handle exception
	    System.out.println("文件无法写入或者文件不存在,请检查。");
	}finally {
		try {
			fileWriter.close();
			scanner.close();
		}catch(IOException e) {
			System.out.println("写入数据关闭失败,请检查。");
		}
		
	}
}
}

运行结果:
请输入第1个字符串:
DFGHHJJK
请输入第2个字符串:
FGGHHJJKL
请输入第3个字符串:
数字
请输入第4个字符串:
sss
录入完成
https://www.cnblogs.com/wengjingming/p/10288021.html
请添加图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

十六 界面设计

1. AWT

图形用户界面(Graphic User Interface,GUI)是实现人机交互的窗口。
Java早期提供的AWT包(Abstract Window Toolkit,AWT)中包括了图形用户界面设计的基本类库,
它是Java设计GUI的核心,为用户提供基本的界面组件。
这些类都放在了java.awt包中。
在这里插入图片描述
AWT由下面几部分组成:
Component(组件):按钮、标签、菜单等组件。
Container(容器):扩展组件的抽象类Window、Panel、Frame等。
LayoutManager(布局管理器):定义容器中各组件的放置位置和大小等。
Graphics(图形类):与图形处理相关的类。
AWT的缺点是重量级组件,耗费资源多;图形用户界面依赖于本地系统,失去了统一的风格。

2. Swing

Swing组件是在AWT组件基础上发展而来的轻量级组件。
它提供了AWT所能提供的所有功能并进行了扩充。
组件均用Java语言进行开发。
界面风格统一,更为美观。
组件都放在javax.swing包中。
在这里插入图片描述

JFrame类
JFrame(窗口)类是Container(容器)类的间接子类。一个JFrame对象就是一个窗口,可以容纳其他组件。JFrame类的常用方法:
在这里插入图片描述
在这里插入图片描述
窗口菜单
一个窗口菜单由多个组件共同构成。
1、JMenuBar类
JMenuBar(菜单条),用于创建一个菜单条。一个窗口中只能有一个菜单条,并且只能添加到窗口顶端。
Frame添加JMenuBar的方法是:
setJMenuBar(JMenuBar menubar)
2、JMenu类
JMenu(菜单)类,用于创建菜单。
一个菜单条中可以添加多个菜单对象。一个菜单中可以添加另一个菜单,实现菜单的嵌套。
JMenu类的常用构造方法:
JMenu(String s)
3、JMenuItem类
JMenuItem(菜单项)类用于创建菜单项,每一个菜单中可以包含多个菜单项。JMenuItem类的常用构造方法:
JMenuItem(String text)
JMenuItem(String text,Icon icon)
常用组件
1、JButton按钮类
JButton类用于创建普通按钮。常用的构造方法:
public JButton(String text)
public JButton(String text,Icon icon)
2、JRadioButton单选按钮类和ButtonGroup按钮作用域类
JRadioButton类用于创建单选按钮。JRadiaoButton类的构造方法:
public JRadioButton(String text)
public JRadioButton(String text,boolean selected)
3、JCheckBox复选框用域类
4、JLabel标签
5、JTextField文本框
6、JPasswordField密码框
7、JTextArea文本区JScrollPane滚动条视图
JTextArea(String text)
JTextArea(int rows,int columns)
JTextArea(String text,int rows,int columns)
JScrollPane类属于容器,用于创建一个滚动条视图。与TextArea类配合,当文本区内容超出显示范围时显示滚动条。

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;

public class Test {
public static void main(String[] args) {
	MyFrame frame =new MyFrame();
    //frame.setTitle("小窗口");
    //frame.setSize(300,200);
    //frame.setLocation(100,100);
   
    }
}
 class MyFrame extends JFrame{
	 JMenuBar bar;
	 JMenu menu1, menu2,menu3,menu4,subMenuZoom;
	 JMenuItem itemNew,itemFangDa,itemSuoXiao;
	 JButton Button1,Button2;
	 public MyFrame() {
		 super();
		 init();
	 }
	 public void init() {
		 setTitle("这是一个菜单实例");
		 //实例化菜单条、菜单
		 bar=new JMenuBar();
		 menu1=new JMenu("文件");
		 menu2=new JMenu("编辑");
		 menu3=new JMenu("格式");
		 menu4=new JMenu("查看");
		 //实例化子菜单
		 subMenuZoom =new JMenu("缩放");
		 //实例化菜单项
		 itemNew=new JMenuItem("新建");
		 itemFangDa=new JMenuItem("放大");
		 itemSuoXiao=new JMenuItem("缩小");
		 //把子菜单加入到菜单中
		 menu4.add(subMenuZoom);
		 //把子菜单加入到菜单中
		 menu1.add(itemNew);
		 subMenuZoom.add(itemFangDa);
		 subMenuZoom.add(itemSuoXiao);
		 //把菜单添加到菜单条中
		 bar.add(menu1);
		 bar.add(menu2);
		 bar.add(menu3);
		 bar.add(menu4);
		 
		 //把菜单设置为本窗口的内容
		 setJMenuBar(bar);
		 Button1=new JButton("没有效果的按钮");
		 Button2=new JButton("另一个没有效果的按钮");
		 add(Button1);
		 add(Button2);
		 
		 setVisible(true);
		 setSize(600,400);
	     setLocation(100,100);
		 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	 }
 }

在这里插入图片描述
容器类
Java提供了多种容器为程序设计所使用。 容器可分为三类:
顶层容器,包括JFrame、JDialog、JApplet、JWindow。
普通容器,包括JPanel、JScrollPane、JSplitPane、JTabbedPane、JOptionPane。
特殊容器,包括JInternalFrame、JLayeredPane、JRootPane、JToolBar。
1、Jwindow
2、JPanel
JPanel类,Java中最常用的轻量级容器之一,默认布局管理器是FlowLayout。
JPanel可以容纳其他组件,之间可以嵌套,但本身不可见,需要加入到顶层容器。
3、JScrollPane
JScrollPane提供轻量级组件的 Scrollable 视图用于管理滚动条。用于TextArea文本框中,它不支持重量级组件。常用构造方法:
JScrollPane()
JScrollPane(Component view)

3. 布局

每个容器使用布局管理器对存放其中的组建进行位置、大小的管理。
使用布局管理器后,组件在容器中的大小和位置完全由布局管理器控制和管理,程序员不需要也不能再对组件的位置和大小进行控制。
每一种容器都有默认的布局管理器。
可以用setLayout()方法重新设置布局管理器。
1、FlowLayout
java.awt.FlowLayout是流式布局管理器。把所有组件行进行排列,一行满了后自动排到下一行。
组件的显示位置随着窗口的缩放而发生变化,但顺序不变。位置与添加顺序密切相关,使用时要按一定的顺序进行添加。它是JPanel的默认布局管理器。构造方法:
public FlowLayout()
public FlowLayout(int align,int hgap,int vgap)
参数align:FlowLayout.LEFT(左对齐)、FlowLayout.RIGHT(右对齐)、FlowLayout.CENTER(居中对齐)、FlowLayout.LEADING(与容器方向开始边对齐)和FlowLayout.TRAILING(与容器结束边对齐)。

import javax.swing.*;
import java.awt.*;
class FrameWithFlowLayout extends JFrame
{
	JButton button1,button2,button3;
	JTextField text1,text2;
	FlowLayout f;
	
	void display()
	{
		f=new FlowLayout();//创建FlowLayout布局
		setLayout(f);//设置当前窗口的布局管理器
		setTitle("流式布局管理器示例");//设置窗口标题栏
		
		button1=new JButton("第一个按钮");//创建按钮对象
		button2=new JButton("第二个按钮");
		button3=new JButton("第三个按钮");
		add(button1);//将按钮加入窗口
		add(button2);
		add(button3);
		
		text1=new JTextField(10);//设置文本框对象
		text1.setText("第一个文本框");//设置文本框的初始内容
		text2=new JTextField(10);
		text2.setText("第二个文本框");
		add(text1);//将文本框加入窗口
		add(text2);
		setSize(300,200);//设置窗口大小
		setVisible(true);//设置窗口为可见
		setDefaultCloseOperation(EXIT_ON_CLOSE);
	}
}
public class Example13_06
{
	public static void main(String[] args)
	{
		FrameWithFlowLayout flow = new FrameWithFlowLayout();
		flow.display();
	}
}

2、BorderLayout
java.awt.BorderLayout称为边框布局管理器,把一个容器分成五个区域,这五个区域分别是东西南北中。五个区域的常量标识为:EAST、WEST、SOUTH、NORTH、CENTER。BorderLayout是JFrame的默认布局管理器。
3、GridLayout
java.awt.GridLayout称为网格布局管理器,它将容器划分成网格结构,每一个网格中可以放置一个组件。所有组件的大小都相同,均填充满整个网格。
这些组件按照添加顺序从左到右,从上到下加入到网格中并显示。构造方法:
public GridLayout()
public GridLayout(int rows,int cols)
public GridLayout(int rows,int cols,int hgap,int vgap)
参数rows和cols可以有一个值为零,表示可以将任意数量的对象置于行中或列中。
【例13.8】GridLayout应用举例,简单电话拨号界面设计。根据按键的分布情况,首先定义一个3行4列的网格,然后在每个网格单元中添加一个相应的按键。

import java.awt.*;
import javax.swing.*;
class FrameWithGridLayout extends JFrame{	
void display()	{		
setTitle("网格布局管理器示例");		
JTextField text=new JTextField(20);		
add(text, BorderLayout.NORTH);				
JPanel p = new JPanel();//设置一个JPanel容器对象       		
//将JPanel的布局管理器设置为网格布局管理器,		
//网格为4行3列,网格之间行、列间距均为4个像素		
p.setLayout(new GridLayout(4,3,4,4));		
String[] name = {"1","2","3","4","5","6","7","8","9","*","0","#"};		
for (int i = 0; i < name.length; ++i)			
p.add(new JButton(name[i]));//JPanel的各个网格中加入按钮对象		add(p);//将JPanel容器加入到窗口中,默认为中间位置		pack();//设置窗口为合适大小				
setVisible(true);		
setDefaultCloseOperation(EXIT_ON_CLOSE);	}}
public class Example13_08{	
public static void main(String[] args)	{		
new FrameWithGridLayout().display();	}
}

4、GridBagLayout
java.awt.GridBagLayout称为网格包布局管理器,不需要组件大小相同就可以按水平、垂直或沿着基线对齐。
GridBagLayout中的组件可以占用一个或多个网络单元格,但这些组件的具体放置位置和放置方式需要通过GridBagConstraints类的实例进行设置。
5、CardLayout
java.awt.CardLayout称为卡片布局管理器。
把添加的每个组件像卡片一样叠加在一起,每次只显示最上面的一个组件。
卡片的顺序由组件对象本身在容器内部的顺序决定。
CardLayout 定义了一组方法,这些方法允许应用程序按顺序地浏览这些卡片,或者显示指定的卡片。
6、BoxLayout
javax.swing.BoxLayout称为盒式布局管理器。允许以水平或垂直方向布置多个组件,这些组件排在一行或一列。
BoxLayout是Box容器的默认布局管理器。构造方法:
public BoxLayout(Container target,int axis)
axis为布置组件时使用的轴,常用的值有BoxLayout.X_AXIS(指定组件从左到右排在一排)和BoxLayout.Y_AXIS(指定组件从上到下排在一列)。
在实际应用中,多使用 Box 类的静态方法设置布局,而不是直接使用 BoxLayout。
7、null(空布局)
容器使用setLayout(null)方法将布局设为空。
添加进入容器的组件使用setBounds(int x,int y,int width,int height)方法指定该组件在容器中的位置和大小。

4. 事件处理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
class ComponentWithActionEvent extends JFrame 
			implements ActionListener//实现动作监视器接口
{//创建一个窗口界面
	JButton button_up,button_down,button_first,button_last;//声明所需的按钮组件
	JLabel label1,label2,label3;//声明所需的JLabel组件
	JPanel panel;//声明一个JPanel容器,用于图片的载入和显示
	CardLayout card;//声明一个CardLayout布局管理器,用于组件的叠加存放
	
	public ComponentWithActionEvent()
	{
		button_up=new JButton("上一张");
		button_down=new JButton("下一张");
		button_first=new JButton("第一张");
		button_last=new JButton("最后一张");
		
		label1=new JLabel();//创建JLabel,用于装入图片
		label2=new JLabel();
		label3=new JLabel();
		label1.setIcon(new ImageIcon("1.png"));//将图片加入label,实现图片的显示
		label2.setIcon(new ImageIcon("2.png"));
		label3.setIcon(new ImageIcon("3.png"));
		
		panel=new JPanel();//创建一个JPanel容器,用于载入各个JLabel组件
		card=new CardLayout();//将JPanel容器的布局管理器设为CardLayout,
		panel.setLayout(card);//实现图片的逐一显示
		
		panel.add(label1);//将各个JLabel组件加入到JPanel容器
		panel.add(label2);
		panel.add(label3);
		
		card.first(panel);
		add(panel,BorderLayout.CENTER);//将JPanel容器加入到窗口的中间位置
		add(button_up, BorderLayout.WEST);//将各个按钮组件加入到窗口的指定位置
		add(button_down, BorderLayout.EAST);
		add(button_first, BorderLayout.NORTH);
		add(button_last, BorderLayout.SOUTH);
		
		button_up.addActionListener(this);//注册监视器。用当前对象this作监视器,
		button_down.addActionListener(this);//因为当前对象所在的类实现了ActionEvent
		button_first.addActionListener(this);//接口,所以它可以作监视器
		button_last.addActionListener(this);
		
		setTitle("动作事件示例");
		setSize(260,260);
		setVisible(true);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	//actionPerformed是ActionEvent接口中的方法,必须定义
	//当事件发生后,该方法就会被调用,并将事件对象传递给参数e
	public void actionPerformed(ActionEvent e)
	{//一个监视器同时监视4个按钮,所以要判断是哪一个事件源产生的事件
		if(e.getSource()==button_up)//监听up按钮,显示上一张图片
			card.previous(panel);
		else if(e.getSource()==button_down)//监听down按钮,显示上一张图片
			card.next(panel);
		else if(e.getSource()==button_first)//监听first按钮,显示第一张图片
			card.first(panel);
		if(e.getSource()==button_last)//监听last按钮,显示最后一张图片
			card.last(panel);
	}
}
public class Example13_12
{
	public static void main(String[] args)
	{
		new ComponentWithActionEvent();
	}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
例: 使用鼠标适配器类,监听鼠标在按钮上的点击动作,显示点击的按键、点击的次数和点击时鼠标的坐标位置。

import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
class FrameWithMouseEvent extends JFrame
{
	JButton button1,button2;
	JTextField text;
	Listen listen;
	void display()
	{
		button1 = new JButton("按钮1");//创建待监听的按钮组件1
		button2 = new JButton("按钮2");//创建待监听的按钮组件2
		text = new JTextField(10);//创建JTextField组件,用于显示监听信息
		
		listen = new Listen();// 创建一个继承了MouseAdapter类的监听器
		listen.setButton(button1, button2, text);//传递待监听对象到监听器
		
		button1.addMouseListener(listen);//注册监听器
		button2.addMouseListener(listen);
		
		add(button1, BorderLayout.SOUTH);
		add(text, BorderLayout.CENTER);
		add(button2, BorderLayout.NORTH);
		
		setSize(300, 200);
		setVisible(true);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
	}
}
public class Example13_13
{
	public static void main(String[] args)
	{
		new FrameWithMouseEvent().display();
	}
}
class Listen extends MouseAdapter
{//定义一个监听类,继承MouseAdapter类,实现鼠标动作的监听和处理
	JButton button1;
	JButton button2;
	JTextField text;
	public void setButton(JButton b1,JButton b2,JTextField t)
	{//传递组件对象到类中
		this.button1=b1;
		this.button2=b2;
		this.text=t;
	}
	public void mouseClicked(MouseEvent e)
	{//重写mouseClicked方法,完成鼠标点击事件的处理
		if(e.getSource()==button1)
		{//鼠标点击的是按钮1
			if(e.getButton()==MouseEvent.BUTTON1)//鼠标左键被按下
				text.setText("点击了"+button1.getText()+"的左键 ");
			if(e.getButton()==MouseEvent.BUTTON3)//鼠标右键被按下
				text.setText("点击了"+button1.getText()+"的右键 ");
			text.setText(text.getText()+e.getClickCount()+"次"+";"
				+"点击的坐标位置是:"+e.getX()+","+e.getY());
		}
		else if(e.getSource()==button2)
		{//鼠标点击的是按钮2
			if(e.getButton()==MouseEvent.BUTTON1)//鼠标左键被按下
				text.setText("点击了"+button2.getText()+"的左键 ");
			if(e.getButton()==MouseEvent.BUTTON3)//鼠标右键被按下
				text.setText("点击了"+button2.getText()+"的右键 ");
			text.setText(text.getText()+e.getClickCount()+"次"+";"
				+"点击的坐标位置是:"+e.getX()+","+e.getY());
		}
	}
}

5. 其他事件

KeyEvent是键盘事件类
ItemEvent事件
FocusEvent事件
DocumentEvent事件
窗口事件

6. 对话框

对话框用于用户和程序之间进行信息交换。
类JDialog(对话框)及其子类(用户定义)的对象表示对话框。
JDialog类和JFrame类一样都是Window的子类,同属于顶层容器。
对话框分为有模式对话框和无模式对话框两类。
在创建一些简单、标准的对话框时,主要使用javax.swing.JOptionPane类来完成。
如果想创建一个自定义的对话框,则可以使用javax.swing.JDialog类。
消息对话框
消息对话框showMessageDialog是显示指定内容的、带有一个按钮的对话框。用于显示一些提示信息,它是一个有模式对话框。创建消息对话框的常用方法为:

public static void
showMessageDialog(Component parentComponent, Object message,String title, int messageType)

它是JOptionPane类的一个静态方法,有4个参数:
参数parentComponent,用于确定显示对话框的父窗口,并在这个父窗口的中间显示。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值