JavaSE笔记总汇

目录

自我介绍

**本人是一名在校大二学生,以下是我学习JavaSE的笔记分享,希望可以帮助大家 **

**哪里有错误的地方希望大家及时纠正 **

boos

数据类型.

先简单介绍java中的语法可以速速过一遍

1.基本类型(八个)

数值型

整型类型

  • byte型:1字节 8bit位 第一位是符号位

    byte a = 127;
    

  • short型:2字节

    short b = 32767;
    
  • int 型:4字节

    int c = 123;
    int c = 0b123;//二进制
    int c = 0123;//八进制
    int c = 0x123;//十六进制
    
  • long型:8字节

    long d = 123l;
    long d = 123L;//数字后缀L或l
    

0b0B开头数字 为二进制

0开头数字为 八进制

0x0X开头为16进制

浮点类型

  • float :4字节

  • 单精度,尾数可以精确到7位有效数字

    float e = 123.4f//数字后缀加f或F
    
  • double: 8字节

  • 双精度,精度是float的两倍。通常采用此类型。

    double f = 123.4
    
    %%java浮点型默认为double类型%%
    • 十进制数形式,例如: 3.14 314.0

    • 科学记数法形式,如 3.14e2 3.14E2

字符型 (char)2字节

  • 使用了Unicode 编码 兼容了Ascll码
  • 可以参与算术运算,本质是使用了Unicode编码表中对应的10进制编号来进行计算

逻辑型(boolean)

  • ture

  • false

2.引用类型

String类型(字符串)(相关API会在API.md文件里面介绍)

String是一个类,String的变量是对象的管理者而非所有者

  • '+'相当于字符串连接符

  • String s = "abcd";

  • 1.任意八种基本数据类型的数据与String类型只能进行连接“+”运算(字符串连接),且结果一定也是String类型

  • 2.String类型不能转为其他的基本类型

	String s = new String("a string");//创建一个String的对象,用“a string”初始化这个对象
 String a = "hello";
       String b = "world";
       String c = a + b;
       String d = a + b + 12;
       System.out.println(a);
       System.out.println(b);
       System.out.println(c);
       System.out.println(d);
  
运行结果:
  
  ```java
  
  hello
  world
  helloworld
  helloworld12
  
  Process finished with exit code 0
  
  • 输入字符串

    in.next();读入一个单词,单词标志是空格(包括空格、tap和换行)

    in.nextLine();读入一整行

  • 比较两个String

    if(input == "bye"){//比较是否同一个
    	····
    }
    if(input.equals("bye")){//比较内容是否相同
    	····
    }
    

    Strings应该用.equals 来比较

接口(interface)(后面会介绍)

数组([])(后面专门介绍)

包裹类型(特殊的数据类型)

  • 它用于将基本数据类型包装成对象

基本数据类型的转换

  • 八种基本类型除了boolean类型以外,其他七种类型之间可以相互转化

转换规则:

1.默认转换

将取值范围小的的类型自动提升为取值范围大的类型

(byte,char,short(byte->short))->(int)->(long)->(float)->(double)

4个字节float比8个字节long取值范围要大

原因: 浮点数在存储时结构与整数计算方式不同

2.强制转换

容量大的转换为容量小的

格式 : (强制转换的类型) 变量名

int a =258;
byte b = (byte)a;

注:有多种类型的数据混合运算时,系统首先自动的将所有数据转换成容量最大的那一 种数据类型,然后再进行计算会有精度损失

基本数据类型的包装类

Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面
向对象的,这在实际使用时存在很多的不便,为了解决这个不足,在设
计类时为每个基本数据类型设计了一个对应的类进表示,这样八个和基
本数据类型对应的类统称为包装类

基本数据类型 包装类(包裹类型)
byte Byte
short Short
char Character
int I nteger
long Long
float Float
double Double
boolean Boolean

例如:Integer

声明

		Integer a=new Integer(12);
        Integer b=new Integer(10);

调用非静态方法

int compareTo();
boolean equals();
 //调用非静态方法
        System.out.println(a.compareTo(b));//前面大返回1,后面大返回-1,一样大返回0;
        System.out.println(a.equals(b));//比较a与b内容是否相同 与"=="要区分 返回一个boolean类型
        System.out.println(a==b);//比较a与b是否指向一个内容
        //.....

调用静态方法

Integer valueOf
int compare(int a,int b)
Integer.parseInt(String str,int radix);
     //调用静态方法
		Integer c=Integer.valueOf(12);//调用静态方法,返回一个Integer类型变量给C
        Integer d=Integer.valueOf("10");//调用静态方法,把字符串类型(必须是数字)转换为Integer返回一个Integer类型变量
		Integer.parseInt(String str,int radix);//把radix进制对应的str转化为十进制
        System.out.println(Integer.compare(13,10));//前面大返回1,后面大返回-1,一样大返回0;

        System.out.println(Integer.max(13,10));//返回较大的数值
        System.out.println(Integer.valueOf(10));//返回的是引用类型

装箱和拆箱

  • 装箱:

将基本类型可以自动转换为包装类型

Integer a=10;//默认调用Integer.valueOf(a)把10转化为引用类型即Integer类型
  • 拆箱:

将包装类型转化为基本类型

int c=a.intValue();//拆箱
int c=a;//自动拆箱
源码: 
public static Integer valueOf(int i) {//low:-128;high:127
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);    
    }

        Integer a=10;//默认调用Integer.valueOf(a)
        Integer a=10;
        Integer b=10;
        System.out.println(a.equals(b));//结果为true
		System.out.println(a==b);//结果也为true
		Integer c=128;
        Integer d=128;
        System.out.println(c==d);//结果为false
        System.out.println(c.equals(d));//结果为true

由源代码可知,当在方法内部对 -128~127之间进行缓存(数组),在此期间的自动装箱,不会创建新的Integer对象,直接从中获得,(因为a与b传过去对应i的参数都为10,则返回的是同一个对象地址的引用),而超出此区间的每次都会返回新的对象(所以c和d虽然值相等但是返回的都是一个新的对象地址,所有c==d结果为false)

算术运算

分类 运算符
算数运算符 * / + - % ++ --
赋值运算符 +=, -=, /=, *=, %=(扩展赋值运算符), =
比较(关系)运算符 == != > <= >= instanceof
逻辑运算符 & && | || ! ^
位运算符 ~ & | >> << >>>
条件运算符 (条件表达式)? 表达式1:表达式2

算数运算符

1.‘+’

  • 表示正数
  • 进行加法运算(数值+数值,数值+‘字符’,‘字符’+‘字符’)
  • 连接作用 (字符串+字符串,字符串+数值......字符串+其他)=字符串

2.‘-’

  • 表示负数
  • 减法运算(数值-数值,数值-‘字符’,‘字符’-‘字符’)
  • 注:字符串没有减法

3.‘*’

4.‘/’

5.‘++’

  • ++数值:先++再赋值y=x++ +3;//x+3
  • 数值++:先赋值再++y=++x+3;//x+1+3

6.‘--’

7.‘%’

  • 两边为整数
  • 与++同理

赋值运算符 ('=')

  • 将等号右边的值赋值给等号左边

下面举一个特殊的例子:

short s = 3;
s=s+2; ①会出错,因为2为int型
s+=2; ②是对的,+2后会进行隐式转换为s对应的数据类型/= ,*=,-=,%=同理

1688960932459

1688961030282

注:两边数据类型必须一致

比较运算符(结果为true与false)

1.'=='

判断两边数值是否相等(基本类型与基本类型),Boolean与Boolean进行比较,String与String进行比较(引用类型与引用类型),

注意 字符串自己不能比较大小,Boolean与Boolean不能比较大小

2.'!='

数值!=数值,bool!=bool,string!=string;

3.'>'和'<'

数值与数值,字符与数值,字符与字符

4.'>='与'<='

与3同理

5.instanceof

判断左边类型是否与右边类型一致

package homeworkday7.chouxiang;

public class Test {
    public static void main(String[] args) {
        Cuiweiyang dqw=new Dengqinwen();
        Cuiweiyang dqw2=new Dengqinwen2();
        Person zs = new Person();
          dqw.sleep();
          dqw2.sleep();
          Dengqinwen dqw3=(Dengqinwen) dqw;
          zs.feed(dqw3);
    }
}
package homeworkday7.chouxiang;

public class Person {
    public void feed(Cuiweiyang cuiweiyang){
        cuiweiyang.eat();
        if(cuiweiyang instanceof Dengqinwen){//判断cuiweiyang是否与Dengqinwen是一个类型
            System.out.println("转换为Dengqinwen成功!");
        }
    }
}

1689579249022

逻辑运算符

1.& 逻辑与

所有表达式结果为true,结果为true ,否则false

特点:当第一个表达式为false时,还会去继续执行第二个式子

2.&& 逻辑与/短路与

所有表达式结果为true,结果为true ,否则false

特点:当第一个结果为false时,不会再继续执行第二个式子

int a=1;
        int b=2;
        int c=3;
        System.out.println(a>b&(b++)>c);
        System.out.println(b);
        b=2;
        System.out.println(a>b&&(b++)>c);
        System.out.println(b);
false
3
false
2

3.| 逻辑或

当所有表达式结果为false,结果为false,否则为true

特点:当第一个表达式为true时,还会去继续执行第二个式子

4.|| 逻辑或/短路或

当所有表达式结果为false,结果为false,否则为true

特点:当第一个表达式为true时,不会去继续执行第二个式子

int a=1;
        int b=2;
        int c=3;
        System.out.println(a<b|(b++)>c);
        System.out.println(b);
        b=2;
        System.out.println(a<b||(b++)>c);
        System.out.println(b);
true
3
true
2

5.! 逻辑非

!true = false,!false = true;

6.^ 逻辑异或

相同为false 不同为true

条件运算符

(条件表达式)? 表达式 1 : 表达式2

若 条件表达式 为true 去执行表达式 1 ,否则执行表达式 2;

位运算符

1.<< 左移

空位补0,被移除的最高位丢弃,空缺位补0

int a=7;
        int b=4;
        System.out.println(b<<2);
        //0000 0100 4
        //0001 0000 16

2.>> 右移

被移位的二进制最高位是0,右移后,空缺位补0,最高位是1,空缺位补1

        int b=4;
        System.out.println(b>>2);
        //0000 0100 4
        //0000 0001 1

3.>>> 无符号 右移

被移位二进制最高位无论是0还是1,空缺位都用0补

正数

        int b=4;
        System.out.println(b>>>2);
        //0000 0100 4
        //0000 0001 1

负数

		int b=-4;
        System.out.println(b>>>2);
        /*
            0111 1111 1111 1111 
            1111 1111 1111 1100 -4
         */
        /*
            0011 1111 1111 1111
            1111 1111 1111 1110 1073741823
         */

4.& 与

对应两位都为1,则结果为1,否则0

		int a=2;
        int b=4;
        System.out.println(a&b);
        /*
            0000 0010 2
            0000 0100 4 &
            0000 0000
         */

5.^ 异或

对应两位都相等,则结果为0,否则1

		int a=2;
        int b=4;
        System.out.println(a^b);
        /*
            0000 0010 2
            0000 0100 4 ^
            0000 0110
         */

6.| 或

对应两位都为0,则结果为0,否则1

		int a=2;
        int b=4;
        System.out.println(a|b);
        /*
            0000 0010 2
            0000 0100 4 |
            0000 0110
         */

7.~ 非

对所有位取反 1~0 0~1

		int a=2;
        int b=4;
        System.out.println(~a);
        /*
            ~0000 0010 2
             1111 1101 -3(最高位为1)
         */

注:使用byte类型移位时,系统会自动换为int进行位运算

关键字

具有特殊含义的 命名时不可以与关键字重名

标识符

也就是名字,对类名,变量名称,方法名称,参数名称等修饰

标识符命名规则

  • 以字母,下划线_或者$开头,其后可以是字母,数字,下划线或$
  • 如:Aa $h m5 abc69_
  • 如:hello HELLO Hello

标识符命名规范

  • 建议:见名知意,也就是说最好编写单词,如name age
  • 建议:类名的每个单词首字母大写,和驼峰命名法 如:Hello,HelloWorld
  • 建议:变量名称,方法名称,参数名称首字母小写,采用驼峰命名法,如age,getAge
  • 建议:标识符长度不超过15个字符,如avg表示average......

字面值

  • 整数类型字面值,如:99,100,-1;
  • 浮点类型字面值:如:0.1,3.1415926
  • 字符串类型字面值:如:"你好","98","3.1415926"
    • 字符串:表示一次可以存放0个,1个或多个,但是必须使用英文双引号引起来
  • 字符类型字面值 如'a','7'
    • 字符类型:表示一次只能存放一个,并且使用英文单引号引起来
  • 布尔类型字面值 如:true false
    • 布尔类型:表示只有两个取值,分别是 ture false

控制台输入(Scanner类)

import java.util.Scanner;//导包

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

        Scanner in = new Scanner(System.in);//Scanner 后面可以跟任何标识符来做对象,System.in为固定格式
        int a = in.nextInt();//读入整型
        float b = in.nextFloat();//读入浮点型
        double c = in.nextDouble();//读入浮点数
        boolean d = in.nextBoolean();//读入布尔类型
        String b = in.next();
        System.out.println(a);
        System.out.println(b);
    }
}

控制语句

条件语句 - 根据不同条件,执行不同语句

  • if
  • if...else
  • if...else if
  • if...else if ... else if...else
  • switch

if条件结构

if条件结构是根据条件判断之后再做处理

  • if-else
  • if - else if....嵌套

switch语句

1688981437307

switch与if区别

1688981481779

for 循环

初始化-->判断条件 -->true(循环体)-->继续判断

​ |false-->结束

for格式

for(初始化;判断条件;步进){
		循环体
}
eg:for(int i=0;i<9;i++){
		sum+=i;
}

增强for循环

for(元素类型 临时变量:遍历的数组){
	临时变量
}
for(int t : a){
    System.out.println(t);
}

while循环

while与do while

while格式

while(判断条件){
	循环体
}

do while循环(不管满不满足判断条件 都先执行一遍循环体)

do{
循环体
}while(判断条件)

*print与println,printf区别

  • System.out.print();括号内必须含有参数

  • System.out.println();括号内可以不含参数,此时代表newline即换行;

  • System.out.printf();使用方法和C语言类似;

*标签、break、continue

当需要结束多个循环后,则使用标签

label:for(int i=0;i<3;i++){
	for(int j=0;j<3;j++){
        if(j==2){
            break label;
        }
    }
}

break:只能跳出一个循环,要想跳出多个循环时,可以使用如上所示的标签;

续: continue 跳过本次循环,直接执行下次循环

Java中的方法(函数)

  • Java方法是一段可重复使用的代码块,用于执行特定的任务。方法可以接受输入参数并返回一个值。在Java中,方法由方法名、参数列表、返回类型、方法体组成。

方法声明格式:(与函数类似)

[访问权限修饰符 修饰符...] [ 返回值类型 ] 方法名( 形式参数类型 参数名){

			java语句[函数体];

			[return 返回值;]

}
  • 修饰符:这是可选的,告诉编译器如何调用该方法,定义了该方法的访问类型

  • 形式参数:在方法被调用时用于接收外界输入数据

  • 返回值类型:事先约定的返回值的数据类型,如无返回值,必须给出返回值类型void

  • 方法体:方法体必须有{}括起来,在{}中编写完成方法功能的代码

  • 返回值:方法在执行完毕后返还给调用它的环境数据。

    return 返回值; return;(不返回任何数值)

  • 实参:调用方法时 实际传给方法的数据

注:

  • 若方法再同一个类中,调用时,直接调用名字
  • 若方法不在一个类中,调用时:类名.方法名(静态方法)
eg:public class PracticeInClass {
    public static void main(String[] args) {
        print();//同一个类中
    }
    public static void print(){
        for(int i=1;i<10;i++){
            for(int j=i;j<10;j++){
                System.out.print(i+"*"+j+"="+i*j+" ");
            }
            System.out.println();
        }
    }
}

public class PracticeInClass {
    public static void main(String[] args) {
       Function.print();//调用其他类的方法,类名.方法名
    }
}
public class Function {
    public static void print(){
        for(int i=1;i<10;i++){
            for(int j=i;j<10;j++){
                System.out.print(i+"*"+j+"="+i*j+" ");
            }
            System.out.println();
        }
    }
}
public class PracticeInClass {
    public static void main(String[] args) {
       Function.print();
       int a=10;
       int b=9;
        System.out.println(max(a,b));
    }
    public static int max(int a,int b ){
        return a>b?a:b;
    }
}
    //传参的方法

数组

概念

在Java中,数组是一种用于存储多个相同类型元素的数据结构。数组在内存中是连续存储的,每个元素在数组中都有一个唯一的索引,可以通过索引来访问数组中的元素

  • 数组是一组相同数据类型元素的集合,是一个容器
  • 数组本身时引用数据类型,是一个对象
  • 数组可以存储基本数据类型,也可以存储引用数据类型
  • 数组创建时必须指定长度,且长度不可变
  • 数组中每个元素是连续的

如何创建数组

数组声明的两种方法

1. 数据类型 [] 数组名字;eg:int [] a;

2. 数据类型 数组的名字 []; eg:int a []

注:建议使用第一种

数组创建

​ 创建一个容量为5的数组:

import java.util.Arrays;//声明这个包后就可以使用Arrays这个类的方法

public class ArrayPractice {
    public static void main(String[] args) {
        int [] a = new int[5];
        System.out.println(a);//输出的是存储数组元素的内存首地址
        System.out.println(Arrays.toString(a));//直接遍历出来[0, 0, 0, 0, 0]
       //创建数组时,值是已知的,直接进行赋值,就不需要指定长度
        int [] c = new int[]{1,2,3,4,5,6,7};
        //简化版
        int [] d = {1,2,3,4,5,6,7}; 
    }
}

数组的访问与迭代

  • 数组元素的访问:
数组名[索引] 例如:a[0],a[1];
  • 注意
数组的索引从0开始。
索引的数据类型是整数(int)
索引最大值和数组长度始终差1
  • 获取数组名长度
数组名.length
  • 数组元素的遍历

1.for循环

for(int i=0;i<a.length;i++){
	System.out.println(a[i]);
}

2.增强for循环

for(元素类型 临时变量:遍历的数组){
	临时变量
}
for(int t : a){
    System.out.println(t);
}

二维数组

定义:

数组的数组,二维数组的每一个元素是一个一维数组

数组的声明

int[][]a;
int a[][];

注:最好使用第一种声明方式,不容易混淆a的数据类型

数组创建(会自动进行初始换为0)

int [][]a = new int [][]{{1,2,3},{1,2,3},{1,2,3}};
int [][]a = {{1,2,3},{1,2,3},{1,2,3}};
int [][]a = new int[3][5];//定义了一个整型的二维数组 ,这个二维数组有3个一维数组,每一个一维数组包含5个元素
int [][]a = new int [3][]//表示每一个一维数组为null,即没有创建;
    a[0]=new int[4];
	a[1]=new int[5];
	a[2]=new int[4];//对二维数组中的一维数组创建和初始化;

数组遍历

双层循环遍历

eg:
int[][]a = {{1,2,3,3},{4,5,6,4},{7,8,9,5}};
//循环二维数组 从二维数组中每次取出一个一维数组
for(int i=0;i<a.length;i++){
    //遍历每一个一维数组
	for(int j=0;j<a[i].length;j++){
        //遍历一维数组
        System.out.print(a[i][j]+" ");
    }
    System.out.println();
}

重点!!!面向对象

面向过程与面向对象

面向过程和面向对象都是程序设计的一种风格(思想);

面向过程的程序思想

​ 面向过程编程是一种程序设计范式,它将程序分解为一系列的步骤或过程,每个过程依次执行以完成特定的任务。在面向过程编程中,数据和方法是分离的,程序主要关注解决问题的步骤和流程,而不是对象之间的交互。

  • 关注焦点是过程:过程就是操作数据的步骤。如果某个过程的实现代码重复出现,那么就可以把这个过程抽取成一个函数,这样就可以大大简化沉余代码,便于维护。
  • 是以函数(一个一个的功能)为结构进行代码组织
  • 相对于面向对象语言,扩展能力差,后期维护难度较大

面向对象程序思想

​ 面向对象编程(Object-Oriented Programming,OOP)是一种程序设计范式,它以对象为基本单元,将数据和操作数据的方法封装在对象中,通过对象之间的交互来实现程序的功能。面向对象编程强调的是对象的概念和对象之间的关系,使程序更易于理解、扩展和维护。

  • 关注焦点是:在计算机程序设计过程中,参照现实中事物,将事物的属性特征,行为特征抽象出来,用类来表示。
  • 以类为单位进行组织,每种事物都具备自己的属性和行为/功能类:一类问题/分类 String,Math,Array,Scanner······
  • 以类为模版实例化对象,通过对象之间的交换来实现某种功能
  • 是一种设计思维,适合解决复杂问题。代码扩展性强,可维护性高
public class 类名{
    
}

注:千万不要把面向过程和面向对象对立起来,他们是相辅相成的。面向对象离不开面向过程!

区别:面向过程适合简单的问题.

​ 面向对象适合复杂的问题,先宏观的分类设计,具体的某一个步骤落地时,有需要面向过程,他们是相辅相成的

类和对象(面向对象的核心概念)

类:

概念:是具有相同特征的事物的抽象描述,是对对象的概括和总结

类的结构

  • 变量:事物属性的描述(名词)

  • 方法:事物的行为(可以做的事情 动词)

  • 构造方法:初始化对象

  • :没有名字的一个代码块

  • 内部类:在类的内部定义的类

    类的声明格式
    [访问权限修饰符][修饰符]class{
    	
    }
    访问修饰符有两种public,无(默认)
    修饰符:final,abstract
    关键字class用来定义一个类
    Java类名的命名规范:
    类名首字母大写,见名知意,驼峰表示
    

对象:(Everything is an object)

概念:实际存在的该类事物的每个个体 ,也称为实例以类为模板在内存中创建的实际存在的实例

​ eg:人类-->一类群体 概念

​ 张三-->具体的人,对象

  • 同一个类的每个对象有不同存储空间

package day5;

import java.sql.SQLOutput;

public class Car {
    /*
    定义属性 成员变量 直接在类{} 中定义内容,称为类的成员
     */
    String name;
    String color;
    int price;//不需要初始化
    /*
     定义方法 成员方法
     */
    public void run(){
        System.out.println("汽车行驶");
    }
    public void stop(){
        System.out.println("汽车停止");
    }
}

package day5;

public class TestCar {
    public static void main(String[] args) {
        Car car=new Car();//
        //new这个运算符来创建一个对象。new的结果是一个对象的引用
        //new以Car类为模板,块独立的空间在内存中开辟一空间,用来存储对象信息
        /*
        Car(类型)car(变量名)声明一个Car变量=[把右边的对象地址赋值给左边的变量,用变量在后面的程序中表示内存中的对象]
        */
        car.name="宝马";
        car.color="黑色";
		car.run();
    }
} 

变量按位置分

通过上面类的建立我们又得到了新的概念:成员变量和局部变量

成员变量:

  • 可以使用基本数据类型,也可以使用引用数据类型.
  • java中的变量在使用时必须初始化,成员变量可以不对其初始化,系统会对其默认初始化为 null或0;
  • 成员变量可以在成员方法,构造方法,代码块中使用

补:成员变量和局部变量

  • 类中的位置不同

    ​ 成员变量:在类中定义 局部变量:在方法中定义或方法参数

  • 权限修饰不同

    ​ 成员变量:可以使用访问权限修饰符

    ​ 局部变量:不可以使用权限修饰符

  • 初始化不同

    ​ 成员变量:创建对象后,由构造方法初始化

    ​ 局部变量:没有默认初始化,必须定义,赋值

  • 生命周期不同

    ​ 成员变量:随着对象的创建而创建,随着对象的消失而消失

    ​ 局部变量:随着方法的调用而存在,随着方法的调用完毕而消失

  • 内存中的位置不同

    ​ 成员变量:非静态成员变量与对象一起在堆内存中

    ​ 静态成员变量与类信息在方法区中存储

    ​ 局部变量:与方法一起在栈内存中

局部变量(在方法中定义)

  • 系统不会对它自动初始化

在同一类中,成员变量的名字可以与局部变量重名,但是局部变量优先,也就是就近优先

注:  类中的成员和成员方法都必须创建出来的对象来调用

类的构造方法

  • 定义:构造方法与类名相同,且没有返回值,且不需要void修饰
Car bmcar = new Car();
  • 特点:类中没有定义时,会默认有一个无参的构造方法,在无参的构造方法中为成员变量赋初始值;我们还可以定义有参的构造方法,通过有参构造方法为成员变量赋初值

    一旦类中定义了有参的构造方法,那么默认的无参构造方法便会失效,调用不到,如果需要,需要显示的定义无参构造方法

  • 作用:用来为新创建的对象中的成员变量进行初始化

 public Car(){
        System.out.println("无参构造方法");
        name=null;
        color=null;
        price=0;
    }//无参构造
 Car car=new Car();// Car car=new Car();//无参调用
public Car(String n,String m,int p){//有参构造
        System.out.println("有参构造方法");
        name=n;
        color=m;
        price = 300000;
    }
Car dzcar=new Car("大众","黑色",30000);//有参调用

方法重载

既然类的构造可以同名,那么我们写的方法能不能重名呢?

这就是方法的重载

  • 概念:方法的重载是指一个类中具有相同的名字,但参数个数不同的多个方法
  • 如何区分名称相同的多个方法:通过参数的个数,类型,顺序来区分

注:但系统无法区分返回值类型,所以方法的重载跟返回值类型没有任何关系

    public Car(){
        System.out.println("无参构造方法");
        name=null;
        color=null;
        price=0;
    }
    public Car(String n,String m,int p){
        System.out.println("有参构造方法");
        name=n;
        color=m;
        price = 300000;
    }//这俩方法就是方法的重载

对象与引用

  • Java语言中除了基本数据类型以外都属于引用类型
  • Java中的对象是通过引用对其操作的
class Car{
	String name;
	String color;
	int price;
}
Car bm = new Car();
  • 右边的“new Car()”,是以Car类为模板,在堆空间里创建一个Car类对象
  • 左边的“Car bm”创建了一个Car类型引用变量。所谓Car类的引用,就是以后可以用来指向Car对象的对象引用
  • "="操作符使对象引用指向刚创建的那个Car对象(即把对象存储的地址赋值给了bm)
我们可以把这句拆开两部分理解
Car bm;
bm = new Car;
这样写,就比较清楚了,有两个实体;一个是对象引用变量,一个是对象本身
  • 参数传递:

    两种:值传递与引用传递

  • 值传递:(参数类型是基本数据类型)方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值。

  • 引用传递:(参数类型是引用数据类型参数)也称为传递址。方法调用是,实际参数是对象,这时实际参数与形式参数指向同一个地址,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,这个结果在方法结束后被保留了下来,所以方法执行中形式参数的改变将会影响实际参数。
    注:基本类型传递的是该数据值本身。引用类型传递的是对对象的引用,而不是对象本身 。但是无论是值传递还是引用传递,其本质都是传值,所以也都统称值传递

this关键字

由于存在方法中参数的名字与类中名字重复的问题,我们该如何区分呢?

java中的this关键字就可以解决这种问题

  • this关键字代表前对象
  • 使用this关键字引用成员变量
  • 使用this关键字引用成员方法构造方法

有了this关键字我们就可以来区分成员变量局部变量

注:一个方法中没有跟他同名的局部变量,可以不加this关键字

this表示当前对象,即表示当前调用此方法的对象

1689387027564

static关键字

概念:可以用来修饰属性,方法,代码块,内部块

  • 随着类的加载而加载
  • 优先于对象存在
  • 修饰的成员,被所有对象共享

static修饰的属性(静态成员变量)

静态属性是类的所有对象共享的,静态属性在内存中只有一个

  • 静态变量的默认值规则和实例变量一样
  • 静态对象在本类中,可以在任意方法代码块,构造器中直接使用
  • 可以通过类名.静态变量直接访问,也可以通过对象.静态变量的方式访问(更推荐类名.静态变量的方式)
static String name="zhangsan";

static修饰的方法(静态方法)

  • 静态方法在本类中,可以在任意方法代码块,构造器中直接使用
  • 调用方式类似于静态变量
  • 注:在static方法内部只能访问类的static修饰属性方法,不能访问类的非static的成员,但是非静态方法可以调用静态成员变量
  • 因为不需要实例就可以访问static方法,因此static方法内部不能有thissuper

使用环境:当某种属性重复使用或某种方法与成员变量无关时,应设计成静态属性和静态方法,这样可以提高程序运行效率,节省运行所需内存

代码块

实例代码块

{
	代码块
}

在创建对象时,被自动调用执行

静态代码块

static{
	代码块
}

在类被加载时,被自动调用执行

  • 类什么时候被加载

    • 通过类名访问类中静态成员时,类会被加载

    • 在一个类中使用main方法,也会加载类

    • 创建对象时也会被加载

类只被加载一次,所以静态代码块只在类加载时执行一次

public class CodeBlock {
    static String name="lisi";
    {
        System.out.println("code1");
    }
    static{
        System.out.println("static code2");
    }

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

1689407795785

如果有多个静态代码块或实例代码块,他们会按照顺序加载

包(Package)

为方便管理类(按照不同的功能管理类),解决同名问题的发生

  • 使用package关键字修饰包

  • 类名(全类名)=包名(地址)+类名简称

    day1.Car
    
  • 不能一次导入两个名称相同不同包的类若想使用,则第二个要用全类名

作用

  • 按照不同功能管理类
  • 避免类重名
  • 控制访问权限

包名命名规范

  • 在包名中,可以使用.号来区分包的级别;包名一般情况下是小写

    第一级 指该项目类型,如com,org,gov等

    第二级 指项目所开发或者运行的公司名称 如 sun huawei

    第三级 指项目的名称,如 bcms,oa,erp,cms等

    第四级 指项目模块的名称,如bean.action,exception等

导入外部包的类 关键字"import"

在一个类中使用其他包中的类时,需要先使用import关键字导入进来

只有java.lang包中的类比较特殊,使用时不需要导入

访问权限修饰符

用来修饰类中的成员,控制是否可以被访问

public

公共权限,在系统中哪里都可以访问

  • 修饰类(包含内部类),方法(构造方法,成员方法),成员变量
  • 如果一个类使用public修饰则文件名必须与类名一致
  • 类名前面可以没有public

protected

受保护权限,在本类,同包类,不同包子类可以访问

  • 内部类,修饰方法(构造方法,成员方法),成员变量

(啥也不写)

默认权限,在本类,同包类访问

  • 修饰类(包含内部类),方法(构造方法,成员方法),成员变量

private

私有权限 只能在本类中使用

  • 修饰类(包含内部类),方法(构造方法,成员方法),成员变量
package day7.test1;

public class Test1 {
    public String name="张三";
    String name1="李四";
    private String name2="王麻子";
    protected String name3="666";

    public static void main(String[] args) {
        Test1 a=new Test1();
        System.out.println(a.name);
        System.out.println(a.name1);
        System.out.println(a.name2);
        System.out.println(a.name3);

    }
}
class TestTest2{
    public static void main(String[] args) {
        Test1 a=new Test1();
        System.out.println(a.name);
        System.out.println(a.name1);
//        System.out.println(a.name2);error//只能在private所属类调用
        System.out.println(a.name3);
    }
}

package day7.test1;

public class TestTest {
    public static void main(String[] args) {
        Test1 a=new Test1();
        System.out.println(a.name);
        System.out.println(a.name1);
        //System.out.println(a.name2);error//私有的private只能在所属类调用
        System.out.println(a.name3);
    }
}

package day7;

import day7.test1.Test1;

public class Test {
    public static void main(String[] args) {
        Test1 a=new Test1();
        System.out.println(a.name);
//        System.out.println(a.name1);//默认修饰符只能在所在包调用
        //System.out.println(a.name2);//私有的private只能在所属类调用
//        System.out.println(a.name3);//protect修饰的只能在所属包调用或者不同包子类
    }
}

面向对象语言三大特征(重点!!!!)

封装

概念:将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问(总结:控制)

案例一:将类中属性私有化

package day7.test3;

public class Test3 {
    private String name;//将名字私有化,使用方法来控制
    private int age;
    //在本类中使用方法对私有的name进行初始化,方便控制name的初始化
    public void setName(String naem){
        if(name.length()<10){
            this.name=name;   
        }
    }
    public void setAge(int age) {
        if(age>=0&&age<150){
            this.age=age;
        }
    }
    public String getName(){
        return name;
    }
    public int getAge() {
        return age;
    }
}

package day7.test3;

public class Test3Test {
    public static void main(String[] args) {
        Test3 a=new Test3();
        a.setName("张三");
        System.out.println(a.getName());
    }
}

案例二:将某种方法私有化

java设计模式(模板,固定套路)

解决一类问题的固定方式

单例模式,在系统中执行让某个类只创建一个对象

package day7.test4;

public class Test4 {
    static Test4 test4=null;
    private Test4(){

    }
    //单一对象,只创建一个对象
    public static Test4 getTest4(){
        if(test4==null){
            test4=new Test4();
        }
        return test4;
    }
}
package day7.test4;

import OOPUnderstand.Test;

public class Test4Test {
    public static void main(String[] args) {
        Test4 a=Test4.getTest4();
        Test4 b=Test4.getTest4();
        System.out.println(a);
        System.out.println(b);
    }
}

继承

概念:Java中,继承是一种面向对象编程的特性,允许一个类(称为子类)继承另一个类(称为父类)的属性和方法。通过继承,子类可以获得父类的属性和方法,同时可以添加新的属性和方法或修改现有方法。这种关系体现了“is-a”关系,即子类是父类的一种特殊类型。

  • 子继承父,子就可以拥有父亲的功能

  • 可以将这些共性的属性和行为抽取,这样就不需要在每个类中定义同样属性和行为,只需要类与类之间建立联系即可

好处:

  • 减少代码的冗余,提高了代码的复用性,
  • 有利于功能的扩展

使用环境

  • 满足is-a关系,即什么是什么(猫是动物,狗是动物)

继承语法

通过extends关键字,可以声明另一个A

[修饰符]class 类A{
    ......
}

[修饰符]class 类B extends 类A{
    ......
}
  • 子类会继承父类所有的实例变量实例方法
  • 子类不能直接访问父类中私有的(private)的成员变量方法
  • 在Java中,继承的关键字用的是"extends",表示子类是对父类的扩展
  • Java支持多层继承(继承体系)
  • 一个父类可以拥有多个子类
  • Java只支持单继承,不支持多重继承

Java中如果一个类没有使用extends关键字显示的继承其他类,那么这个类默认继承Object类

1689492299941

所以Java中所有的类都直接或间接的继承了Object类

注:父类不可以使用子类功能

方法的重写

当父类中的方法功能实现不能满足子类需求时,可以对方法进行重写(Override)

子类可以对从父类中继承的方法来进行改造,在程序执行时,子类方法将覆盖父类方法,我么称为 方法的重写 也称方法的覆盖

注:构造方法,静态方法不能重写,成员变量不存在重写

方法重写的规则

  1. 方法名必须和父类相同,参数列表相同(否则调用的是父类方法)

  2. 方法返回值类型与父类保持一致

    即子类重写方法结构与父类一致

  3. 子类方法使用的访问权限不能小于父类被重写的访问权限

    注意:1.父类私有方法不能重写 2.跨包的父类默认权限的方法也不能重写

  4. 子类方法抛出的异常不能大于父类被重写方法的异常

@Override使用说明

@Override是java中定义的注解标签,用来进行标记(进阶部分细讲)写在方法上面,表示此方法是从父类重写而来,用来检测是不是满足重写方法的要求
这个注解就算不写,只要格式满足要求,也是正确的方法覆盖重写。建议保留,这样编译器可以帮助我们检查格式,另外也可以让阅读源代码的程序员清晰的知道这是一个重写的方法

帮助进行语法检测

  • 例如
//父类
public  class Cuiweiyang {
    String name;
    String sex;
    //public static abstract void setName(String name);不能使用static调用
    public void eat() {
        
    }

    public void setName(String name) {
        
    }

    public void abst() {
        
    }

    public void sleep(){
        System.out.println("父亲睡觉");
    }
}
//子类
public class Dengqinwen extends Cuiweiyang {
    public void eat(){
        System.out.println("吃饭");
    }

    @Override
    public void sleep() {
        System.out.println("儿子睡觉");
    }

    @Override
    public void setName(String name) {

    }
    @Override
    public void abst() {

    }

    public Dengqinwen(){

    }
}

super关键字

java类中使用super来调用父类中的操作

  • 用于访问父类中定义的属性

  • 调用父类中定义的成员方法

  • 用于在子类构造器中调用父类构造器

  • @Override
        public void setName(String name) {
            super.setName("123");
        }
    

注意

  • 尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
  • super的追溯不仅限于直接父类还可以是父类的父类
  • superthis的用法相像,this代表本类对象的引用super代表父类的内存空间识的标

误区:不要把super误认为识父类对象,在创建子类对象时,不会创建父类对象,只会将父类中的信息加载到子类对象中存储

继承中的构造方法

  • 子类继承父类时,不会继承父类的构造方法。只能通过“super(形参列表)”的方式调用父类指定的构造方法。
  • 规定super(形参列表),必须声明在构造器的首行
  • 如果在子类构造器的首行没有显示调用super(形参列表),则子类此构器
    默认调用super(),即调用父类中空参的构造器。
  • 这么做是为了保证先对父类成员进行初始化

开发中常见错误

  • 如果子类构造器中既未显示调用父类或本类的构造器,且父类中又没有空参构造器,则编译错误
父类
package day7.extend;

public class Animal {
    private String name;
    private int age;
    public Animal(){
        System.out.println("父类无参初始化");
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;//this指当前对象
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public void eat(){
        System.out.println("eat thing");
    }
}

 子类1
package day7.extend;


public class Cat extends Animal {
    public Cat(){
        //super();
        System.out.println("子类无参初始化");
    }

    public Cat(String name, int age) {
        super(name, age);
        System.out.println("有参构造");
    }

    @Override
    public void eat(){
        System.out.println("eat cat's food");
    }

}
class TestAnimal {
    public static void main(String[] args) {
        Cat mm = new Cat();
        System.out.println("年龄" + mm.getAge());
        System.out.println("名字" + mm.getName());
        mm.eat();
    }
}

运行结果:

1689500705786

无参构造有无super()结果都是一样的,不过推荐写上super()

package day7.extend;


public class Cat extends Animal {

    public Cat(){
        super();
        System.out.println("子类无参初始化");
    }

    public Cat(String name, int age) {
        //super(name, age);
        System.out.println("有参构造");
    }

    @Override
    public void eat(){
        System.out.println("eat cat's food");
    }

}
class TestAnimal {
    public static void main(String[] args) {
        Cat mm = new Cat("咪咪",2);
        System.out.println("年龄" + mm.getAge());
        System.out.println("名字" + mm.getName());
        mm.eat();
    }
}

1689504634339

注释掉super(name,age),后发现虽然调用子类中有参构造,但运行结果依然是未初始化的成员变量

package day7.extend;


public class Cat extends Animal {

    /**
     * chidongxi
     */

    public Cat(){
        super();
        System.out.println("子类无参初始化");
    }

    public Cat(String name, int age) {
        super(name, age);
        System.out.println("有参构造");
    }

    @Override
    public void eat(){
        System.out.println("eat cat's food");
    }

}
class TestAnimal {
    public static void main(String[] args) {
        Cat mm = new Cat("咪咪",2);
        System.out.println("年龄" + mm.getAge());
        System.out.println("名字" + mm.getName());
        mm.eat();
    }
}

1689504754560

取消掉注释后,加上super(name,age),就可以调用父类的有参构造方法对成员变量进行了初始化

注意,不能把子类构造方法中默认会调用父类无参构造方法,如果需要显示的使用super调用,必须放在构造方法的第一行,还可以调用父类中指定的构造方法

1689505060654

抽象类

一个类中没有足够的信息来描绘一个具体的对象,这样的类就是抽象类

被abstract类关键字修饰的类,可能会出现抽象方法

注:抽象类除了不能实例化对象外,类的其他功能依然存在,成员变量,成员方法和构造方法,且没有抽象变量这一说法

特点:

  1. 抽象类不能被实例化(不能创建对象),但可以有构造方法,因为抽象类中含有无具体实现的方法所以不能用抽象类创建对象
  2. 抽象类只能用做基类,表示的是一种关系,继承抽象类非抽象类必须实现其中的所有抽象方法,而已实现方法的参数,返回值要和抽象类中的方法一样,否则,该类也必须声明为抽象类
[访问权限符] abstract class 类名{

}
public abstract class Abstract{
	
}

抽象方法

抽象方法是一种特殊的方法:它只有声明,而没有具体的实现.

[权限修饰符] abstract [返回值类型] 方法名 (参数列表);
public abstract void setName(String name);

抽象方法必须用abstract关键字进行修饰

注:不可以抽象构造方法

抽象方法是为了子类对其的重写,所有不能使用static修饰..被static修饰的为静态方法是可以通过类名直接调用的,但是抽象方法没有任何具体的实现,所以是错误的(静态的方法是不可以被重写的)

多态

多种状态

父类的引用指向子类对象,从而产生多种形态

Animal dog = new Dog();//这里Dog是Animal子类
Animal cat = new Cat();

同一种事物,在不同时刻表现不同状态

二者之间存在直接或间接的继承关系时,父类引用指向子类的对象,即形成多态

  • 在编译期类型是父类,运行期类型是子类时,被称为父类引用指向子类对象
class Animal{
....
}
class Cat extends Animal{
.....
}
class Dog extends Animal{

}
Animal cat=new Cat();
Animal dog=new Dog();//Animal的引用指向Dog的对象

向上转型

package homeworkday7.chouxiang;

public abstract class Cuiweiyang {
    String name;
    String sex;
//public static abstract void setName(String name);不能使用static调用
    public abstract void eat();
    public abstract void setName(String name);
    public abstract void abst();
}
package homeworkday7.chouxiang;

public class Dengqinwen2 extends Cuiweiyang {

    public void eat(){
        System.out.println("喝水");
    }

    @Override
    public void setName(String name) {

    }

    @Override
    public void abst() {

    }
}
package homeworkday7.chouxiang;

public class Test {
    public static void main(String[] args) {
        Cuiweiyang dqw=new Dengqinwen();
        Cuiweiyang dqw2=new Dengqinwen2();
        dqw.eat();//编译期间调用的是父类eat,运行期间运行的是子类eat方法
       
    }
}

1689575220912

编译期间是看父类,运行期间切记是在运行子类方法,就算子类无,父类有,也是运行子类所继承的方法,绝对不是运行父类方法

多态的好处:提高代码的扩展性

class Animal{
	public void eat(){}
    ....
}
class Cat extends Animal{
	public void eat(){
        System.out.println("猫吃鱼");
    }
    .....
}
class Dog extends Animal{
	public void eat(){
    	System.out.println("狗吃骨头");
    }
        .....
}
class Test{
    public static void main(String[]args){
      Animal cat=new Cat();
	  Animal dog=new Dog();//Animal的引用指向Dog的对象  
    }
}

class Person{
    public void Feed(Animal animal){
        animal.eat;//此时传递的参数是子类对象的地址
    }
    //避免了下面的繁琐,提高代码扩展性
    public void Feed(Dog dog){
        dog.eat;
    }
    public void Feed(Cat cat){
        cat.eat;
    }
}

多态环境下对成员方法(非静态)的调用

如上,
cat.eat();
//调用的是子类中的方法

简单来说就是:编译看左边,运行看右边

多态环境下对静态成员方法的调用

package homeworkday7.chouxiang2;
class Animal {
    public static void eat(){
        System.out.println("吃食物");
    }
}
class Cat extends Animal{
    public static void eat(){
        System.out.println("猫吃鱼");
    }
}
class Test{
    public static void main(String[] args) {
        Animal cat=new Cat();
        cat.eat();//调用的是Animal的方法
    }
}

1689566001921

简单的说:编译运行都看左边

注意:变量不存在被子类覆盖写这一说法,只有方法存在覆写

为了实现多态性,我们将子类类型向上转为了父类类型.但是一旦类型上升为父类类型,那么就调用不到子类特有的方法

解决办法:

就行向下转型,把父类转换为子类类型

向下转型(强制类型转换)

格式 [类型]对象名1 = (类型)对象名2

将后面大的父类对象转换为子类小的对象

注:同父类的子类不能相互转化

package homeworkday7.chouxiang;

public class Test {
    public static void main(String[] args) {
        Cuiweiyang dqw=new Dengqinwen();
        Cuiweiyang dqw2=new Dengqinwen2();
        Person zs = new Person();
          dqw.sleep();
          dqw2.sleep();
          Dengqinwen dqw3=(Dengqinwen) dqw;//把Cuiweiyang类型强制转化为子类的Dengqinwen类型
          zs.feed(dqw3);
    }
}

Dengqinwen2 dqw4=(Dengqinwen2) dqw3;//错误,同父类的子类不能相互转化

屏幕截图 2023-07-17 154410

优缺点

优点

  1. 灵活性:多态使用使代码更加灵活,可以通过父类类型的引用变量来引用不同子类对象,从而实现不同对象的统一操作
  2. 可扩展性:通过多态,我们可以方便地扩展和添新的子类,而不需要修改已有的代码
  3. 代码重用:多态可以提高代码的重用性,通过定义父类类型的方法,可以在不同的子类中进行具体实现,减少了代码的冗余

缺点

  1. 性能损失:多态需要在运行时进行动态绑定和方法调用,这会带来一定的性能损失,相比于直接调用子类方法,多台调用会稍微慢一些
  2. 可读性降低:由于多态的特性,代码的行为可能会依赖于运行时的实际对象类型,这可能会增加代码的复杂性和难以理解程度
  3. 限制:多态只能通过父类类型的引用变量来引用子类对象,而不能直接访问子类特有的方法和属性,如果需要访问子类特有的功能,需要进行类型转换

final关键字

用于修饰 ,方法,参数属性

  • 类:不能被定义抽象类或是接口,不可被继承

  • 方法:子类里不可以重写

  • 参数:参数值在方法中不可被修改

  • 属性:定义时就必须直接赋值或者在构造方法中进行赋值,并且后期不能被修改

  • public final class A{
    	final int num=10;
        public final void Test( final int a){
            
        }
    }
    
    

接口(完全抽象的类)

  • 一种更为彻底的抽象,主要用来定义功能
  • JDK8后可以定义4种内容
  1. 静态常量
  2. 抽象方法
  3. 静态方法(JDK8后)
  4. 默认方法(JDK8后)

接口不能被创建对象


interface关键字

使用interface关键字来实现接口的创建

public interface MyInterface {
    int num=0;// 默认为public static int num=0;
    void s();//默认为公共抽象方法即public abstract
}
public interface MyInterface extends A,B{
			.......
}//一个接口可以继承多个接口

注:一个类只能继承一个类,间接继承多个类

一个类可以实现多个接口

一个接口可以继承多个接口

接口不可以构造方法

implements关键字

package day8.jk;

public class Test implements MyInterface {//implements关键字用来实现接口功能

    @Override
    public void Test() {
    }
    @Override
    public void Test3() {
    }
}

一个类可以实现多个接口(多个接口表示多个功能,一个类需要哪些功能就去实现哪些接口)

package day9.animal;

public interface Fly {
    void Fly();
}
package day9.animal;

public interface Eat {
    void eat();
}
public class Bird extends Animal implements Fly,Eat {
    @Override
    public void eat() {
    }
    @Override
    public void Fly() {
    }
}

//Bird实现了Fly接口的功能和Eat接口功能且继承了Animal

一个类如果实现接口

  1. 要么实现所有方法
  2. 要么特此声明为抽象类
  • 实现类和接口之间也是可以有多态关系的
  • 用一个接口,表示一类拥有此功能的类

特性

  • 接口是隐式抽象的,主要是定义功能
  • 接口可以定义静态常量,抽象方法,静态方法,默认方法
  • 一个接口可以继承其他多个接口
  • 接口抽象类一样,不能实例化对象
  • 接口是要被类实现的,一个接口可以被多个实现
  • 当类实现接口的时候.类要实现接口中所有的抽象方法,否则,该类必须声明为抽象的类
  • 接口实现类之间存在多态性

空指针异常

  • 属于运行错误,java.lang.NullPointerException
  • 原因:当引用名称的值为null时,就不能方法某个对象中的属性或方法,如果非要访问则就出现空指针异常
  • 解决办法:在访问某个对象中的属性或方法之前必须保证该引用名称中存放的是对象

API

API(Application Programming Interface)应用程序接口

  • java系统中所提供的一系列类和接口

API文档:官方给广大开发者学习者提供对类和接口功能的说明文档

正则表达式(常用的介绍)

  • 是一种匹配语法,可以使用一些符号来定义一个规则,然后用规则与字符串进行匹配

用法

  • 调用String中的 match 方法来定义规则
  • 也可以使用String中的 split 方法来分割字符
	\d 匹配0~9之间的数字
	\\d 匹配一位数字
	\\d* 匹配多位数字
	\\d{n} 匹配n位数字
	\\d{n, } 匹配至少n位数字
	\\d{n,m} 匹配至少n个数字,最多m个数字
	[1234]匹配的一位数字必须是1,2,3,4
	[1234]* 匹配任意一位数字都是1,2,3,4
	[1-9] 匹配1-9之间的数字
	[a-z] a-z小写字母
	[A-Z] 大写字母
	[a-zA-Z] 可以是大写和小写字母 或[A-z]
	\w 匹配的是单词字符 相当于匹配[1-9] [A-z] [_] 注意区分大小写
	| 在正则表达式中表示 或
	. 也是正则表达式中的符号,匹配任意的字符,使用\\.表示
	
  • 如手机号规则 第一位数字必须 1,第二位数字3,5,7,8,9 其余九位数字为任意数字
boolean b1 = str.matches("1[35789]\\d{9}");//手机号规则
//其中str为字符串对象
  • 再比如邮箱格式 字母+@+ 数字字母
boolean b2 = str2.matches("\\w{6,10}@\\w{2,6}\\.(com|com\\.cn)");//str2为字符串
  • 再比如利用正则表达式可以来使用split方法来切割字符串
String str3 = "ab1cd2ef";
String [] str4 = str3.split("\\d");//把含数字的删掉
  • 或者利用正则表达式来使用replaceAll方法来替换某些元素
String str7 = str3.replaceFirst("\\d", "c");

注:replace方法是不可以使用正则表达式的

Calendar类(关于日期的一些方法)

  • Calendar类

  • 是一个抽象类,在实际使用时实现特定的子类的对象,创建对象的过程对程序员来说是透明的,只需要使用getInstance方法创建即可

  • Calendar c1 = Calendar.getInstance();
    Calendar c1 = new GregorianCalendar();
    

get(Calendar.XXX);

get(Calendar.Year)

  • 获取年份

get(Calendar.MONTH)

  • 获取月份
  • 切记月份是从0开始的

get(Calendar.DAY_OF_MONTH)

  • 获取今天是月份的第几天

get(Calendar.DAY_OF_WEEK)

  • 获取今天是一周的第几天
  • 切记1是周日,从周日开始算

get(Calender.WEEK_OF_YEAR)

  • 获取这周是今年的第几周(注意:国外是从星期日开始的)

get(Calender.HOUR_OF_DAY)

  • 此时是今天的第几个小时

......

set(年,月,日)

  • 设置日期,改变get获得的日期

SimpleDateFormat类(日期格式的一些方法)

Format(Date类型)方法

  • 日期格式化

  • SimpleDateFormat sdf = new SimpleDateFormat("制定格式")
        
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月");
    
  • 调用format方法格式化上面格式日期

  • System.out.println(sdf.(date));//结果是四位数字的年,两位数字的月份
    
    2023年09月
    

    屏幕截图 2023-09-14 230720

parse(String 类型)

  • 把字符串日期转化为Date类型

  • String str = "2004-07-10";
    SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
    Date date2 = sdf2.parse(str);
    System.out.println(date2);
    
    结果:
    Sat Jul 10 00:00:00 CST 2004
    

BigInteger类

  • BigInteger 支持任意精度的整数,可以准确的表达任何大小的整数而不丢失精准度

  • BigInteger 位于 java.math包中

  • BigInteger()括号里必须是字符串

  • BigInteger a = new BigInteger("8888888888888888");
    BigInteger b = new BigInteger("9999999999999999");
    

运算方法add(),subtract(),multiply(),divide()

  • 注意:divide方法里默认无限小数会报错,所以必须使用divide方法规定小数位数

add()

a.add(b);

subtract()减法

a.subtract(b);

multiply()乘法

a.multiply(b);

divide()除法

a.divide(b,3,BigDecimal.ROUND_DOWN);

取三位小数,第三个是取整方式

注:

public int[] plusOne(int[] digits) {
        BigInteger bigInteger =new BigInteger(Arrays.toString(digits));
        BigInteger bigInteger1 = new BigInteger("1");
        bigInteger.add(bigInteger1);
        byte[] bytes = bigInteger.toByteArray();
        Arrays.copyOf(digits,bytes.length);
        for(int i=0;i<bytes.length;i++){
            digits[i]=bytes[i];
        }
        return digits;
    }

这个错误是 NumberFormatException,表示字符串无法转换为有效的数字。在这种情况下,错误发生在 BigInteger 的构造函数 new BigInteger(Arrays.toString(digits)) 中。

根据错误消息中的信息,输入字符串是 "[9]",这意味着 digits 数组中只有一个元素,且该元素的值为 9。然而,Arrays.toString(digits) 方法将数组转换为字符串时,会添加方括号和逗号,导致字符串的格式不符合 BigInteger 的要求

修改后

public int[] plusOne(int[] digits) {
    BigInteger bigInteger = BigInteger.ZERO;
    for (int i = 0; i < digits.length; i++) {
     	bigInteger = 			       bigInteger.multiply(BigInteger.TEN).add(BigInteger.valueOf(digits[i]));
    }
    bigInteger = bigInteger.add(BigInteger.ONE);
    String resultString = bigInteger.toString();
    int[] result = new int[resultString.length()];
    for (int i = 0; i < resultString.length(); i++) {
        result[i] = Character.getNumericValue(resultString.charAt(i));
    }
    return result;
}

BigDecimal()类

  • 由于Double类型运算有精度损失,所以需要使用BigDecimal类来运算,BigDecimal类运算浮点不会有精度损失,但必须传入String类型
  • 运算方法与BigInteger一样

StringBuffer类(用来解决String类的内存浪费问题)

  • 由于String类型创建字符串每次改变后都会创建新的对象,旧的对象地址不会及时的被Java垃圾回收机制回收,造成内存的浪费
  • **所以我们可以引用StringBuffer类来处理字符串,StringBuffer类 带缓冲区,弥补了String不可改变字符串的缺点,char[] valueStringBuffer类中的数组,默认16长度,装满16元素后会进行数组扩容(数组长度*2+2) **

构造方法

public StringBuffer(); 无参构造

public StringBuffer( String ); 有参构造,初始化字符串

public StringBuffer( int );有参构造,初始化缓冲区大小,即底层数组大小

方法

StringBuffer stringBuffer = new StringBuffer();//无参构造
StringBuffer stringBuffer1 = new StringBuffer("abc");//有参构造
StringBuffer stringBuffer2 = new StringBuffer(123);//有参构造指定数组长度

public synchronized StringBuffer append (String str);

stringBuffer.append("abc");
//结果:拼接字符串 相当于String中的'+';

public synchronized StringBuffer insert(int i , String str/int i/char/...)

stringBuffer.insert(1,"123");
//从指定位置,插入字符串/整数/字符...

public synchronized StringBuffer delete(int start , int end);

stringBuffer.delete(1, 3);
//删除指定片段,包含开始位置,不包含结束位置

public synchronized StringBuffer deleteCharAt(int index);

stringBuffer.deleteCharAt(0);
//删除指定位置;

public synchronized StringBuffer replace(int start, int end, String str)

 stringBuffer.replace(0,stringBuffer.length(), "123");
//替换指定区域字符串;

public synchronized StringBuffer reverse()

stringBuffer.reverse();
//翻转字符串

public synchronized String substring(int start)

 String str1 = stringBuffer.substring(1);
 //从某一位置开始截取字符串,返回String类型,注意:不会改变原来类里面的字符串

public synchronized String substring(int start, int end)

String str2 = stringBuffer.substring(1,2)
//截取某一区域的字符串,不包含结束位置,返回值为String,不会改变原来的字符串

StringBuilder类

方法与StringBuffer类似,区别是StringBuffer的方法被synchronized修饰,而StringBuilder方法没有被修饰

StringBuffer与StringBuilder共同点与区别

共同点

  • 字符串是可以改变的,不会创建对象,变的只是底层的数组在改变

区别

  • 由于StringBuffer的方法是被synchronized修饰的,表示一次只允许一个请求进入方法,适合多线程场景
  • StringBuilder 不是加锁的(即它的方法不被synchronized修饰),一次可以允许多个请求进入方法适合单用户操作

synchronized关键字的作用

synchronized关键字的作用如下:

  • synchronized是一种Java语言中的关键字,用于控制多线程并发访问共享资源的一种机制。
  • synchronized可以保证在同一时刻只有一个线程可以访问共享资源,从而避免了多个线程同时访问共享资源所带来的数据不一致性和线程安全问题。
  • synchronized关键字可以将任意的非空对象作为锁,在Java虚拟机JVM中,对象的锁被称为对象监视器(Object Monitor),每个对象都具有其对象监视器,同一时间,一个对象的监视器只能被一个线程所持有。

传参中 ... 类型名 介绍

  • 类型 ... 类型名 表示可变长度的参数,本质是数组

  • 一个参数列表中,只能有一个可变长度参数,而且必须放在参数列表后面

  • public static void test(int ...a){
            System.out.println(Arrays.toString(a));
        }
    

集合

Java集合是一组用于存储和操作数据的类和接口。它们提供了一种方便和灵活的方式来处理数据集合,包括列表、集合、映射等。Java集合框架提供了一组通用的接口和类,用于操作和管理集合对象。

集合分为:单列集合 双列集合

以下为集合结构体系图

image-20231015085028051

单列集合(Collection)

单列集合(又称单元素集合,单元集合),一次只包含一个元素的集合

Collection中的一些方法

先创建集合对象

ArrayList<Integer> arrayList = new ArrayList<>();

public static < T > boolean addAll(Collection<? super T> c, T... elements)

  • 把T...类型数据添加到继承Collection父类的子类中

  • Collections.addAll(arrayList, 1,2,3,4,5,6);//把,1,2,3,4,5,6数据添加到ArrayList集合中
    

public static <T extends Comparable<? super T>> void sort(List < T > list)

  • 对集合进行排序

  • Collections.sort(arrayList);//排序
    

public static < T > void copy(List<? super T> dest, List<? extends T> src)

  • 把src集合复制到dest集合,要保证第一个目标集合的长度大于第二个集合长度,src已有的数据会覆盖dest目标对应位置的元素

  • Collections.copy(arrayList, arrayList1);//*注:第一个目标集合长度要大于第二个
    

public static void swap(List<?> list, int i, int j)

  • 交换集合list中 位置i与位置j的元素

  • Collections.swap(arrayList,1,3);//调换第二个和第四个位置
    

public static < T > boolean addAll(Collection<? super T> c, T... elements)

  • 对目标集合C添加多个元素element

  • Collections.addAll(arrayList1,1,2,3,4,5,7) ;
    

public static < T > void fill(List<? super T> list, T obj)

  • 把集合list的所有元素用 obj类型进行填充

  • Collections.fill(arrayList,1);//即所有元素都为1
    

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

  • 获取集合中元素的最大值

  • Collections.max(arrayList)
    

public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll)

  • 获取集合元素中的最小值

  • Collections.min(arrayList)
    

ArrayList类(对List接口的实现,底层由数组实现)

构造方法

ArrayList arrayList = new ArrayList();//默认为Object类,可以存储任意类型

//ArrayList<E>  E为泛型
ArrayList<String> stringArrayList = new ArrayList<>();//定义String类型
ArrayList<Integer> integerArrayList = new ArrayList<>();//定义Integer类型集合

方法

public boolean add(E e)

integerArrayList.add(10);//自动装箱,10自动转为Integer类型,自动添加内容
		stringArrayList.add("0");
        stringArrayList.add("1");
        stringArrayList.add("2");
        stringArrayList.add("3");
        stringArrayList.add("4");

返回值为boolean类型,添加成功-true 失败-false

public void add(int index , E element)

stringArrayList.add(1,"0");
//在指定位置Index插入数据,不能对没有使用的位置进行插入操作(从0到size(集合元素个数))

public boolean remove(Object O)

stringArrayList.remove("1");
//删除指定元素,并自动进行排序,返回值为boolean类型,如果匹配到对应元素删除成功返回-true 找不到-false

public E remove( int index)

stringArrayList.remove(1);
//删除指定位置(index)元素并返回该元素,并对剩余元素进行排序,并返回该元素

public boolean contains(Object O)

boolean b = stringArrayList.contains("1");
//判断是否包含该元素,返回boolean类型数据

public E get (int index)

stringArrayList.get(2);
//获取指定位置上的元素

public int indexOf(Object O)

stringArrayList.indexOf("1");
//查找指定位置元素,并返回该元素位置
//查找方式:从头查找

public int lastIndexOf(Object O)

stringArrayList.lastIndexOf("1");
//查找指定位置元素,并返回该元素位置
//查找方式:从末尾开始查找

public boolean isEmpty()

stringArrayList.isEmpty();
//判断该集合是否为空,, 是-true 否-false

public int size()

stringArrayList.size();
//返回集合元素个数(即长度)

public void clear()

stringArrayList.clear();
//清空元素个数

Vector类(对List接口的实现,底层由数组实现)

构造方法

Vector<Integer> vector = new Vector<>();//存储Integer类型数据,<>里面也可以是其他类
Vector vector = new Vector();//可以存储任意类型数据

方法

public synchronized boolean add(E e)

public synchronized void add(int index , E element)

public synchronized boolean remove(Object O)

public synchronized E remove( int index)

public synchronized boolean contains(Object O)

public synchronized E get (int index)

public synchronized int indexOf(Object O)

public synchronized int lastIndexOf(Object O)

public synchronized boolean isEmpty()

public synchronized int size()

public synchronized void clear()

用法和ArrayList类方法一样,区别是,Vector类方法被synchronized关键字修饰,当多线程运行时,只允许一个请求调用方法,相比ArrayList是安全的

LInkedList类(对List接口的实现,底层是链表)

构造方法

//1.
LinkedList linkedList = new LinkedList();//可以存储任意类型

//2.
LInkedList<String(可以是任意类型)> stringLinkedList = new LinkedList<>();
//可以存储<>里对应的类型

方法

public boolean add(E e)

public void add(int index , E element)

public boolean remove(Object O)

public E remove( int index)

public boolean contains(Object O)

public E get (int index)

public int indexOf(Object O)

public int lastIndexOf(Object O)

public boolean isEmpty()

public int size()

public void clear()

注:以上三种类是可以存储重复的元素的(对List实现的接口),而对set接口实现的类是不能存储重复的元素

TreeSet(对Set接口的实现,底层是有TreeMap实现的)

注:TreeSet实际是利用TreeMap的key来存储元素,而TreeMap中的Value是位null,底层都是树结构

TreeSet可以给Set集合中的元素进行指定方式的排序。存储的对象必须实现Comparable接口

  • 底层:是树形结构
  • 添加进来的元素可以进行排序(有序的 不是添加的顺序,是元素的自然顺序)
  • 添加顺序是大的元素向右放,小的元素向左放;

构造方法

  • 创建对象方法:
    	TreeSet set = new TreeSet();//指存储任意类型元素
    	TreeSet<Integer> set = new TreeSet<>();//指存储Integer元素(不仅可以是Integer可以是其他类,<>里面的也称为 java中的泛型)
    

TreeSet方法

public boolean add(E e)

  • 对集合添加元素,对应E类型

  • TreeSet<Integer> set = new TreeSet<>();	
    	set.add(3);
        set.add(4);
        set.add(2);
        set.add(1);
        set.add(3);
    

public void clear()

  • 删除集合中所有的元素

  • set.size();
    

public boolean contains(Object o)

  • 判断元素(对象)是否在集合中存在

  • set.contains(1);//是否存在某元素
    

public boolean isEmpty()

  • 判断集合是否为空.

  • set.isEmpty
    

public boolean remove(Object o)

  • 删除集合中的指定元素(对象) O

  • set.remove(1);//删除元素1
    

public E pollLast()

  • 删除集合中最后的一个元素(对象)并返回

  • set.pollLast();//删除并返回最后一个元素
    

public E first()

  • 返回集合第一个位置的元素

  • set.first();//返回第一个元素
    

public E pollFirst()

  • 删除集合中第一个元素返回这个元素

  • set.pollFirst();//删除并返回第一个元素
    

public E last()

  • 返回集合中最后一个元素

  • set.last();//返回最后一个个元素
    

**. . . . . . **

HashSet(对Set接口的实现,底层是HashMap实现的)

Set接口

不能存储重复元素

HashSet

无序的(既不是添加顺序,也不是按元素自然顺序)

不会重复的原因:

  • 底层使用hashCode()和equals()方法;
  • 用内存计算一个hash值(整数),用hash值比较速度块,

注:hash是不安全的,有可能两个内容不一样,hash值一样

当hash值相同时,调用equals()方法,
这样效率提高,也保证安全了

构造方法

  • HashSet<String> stringHashSet = new HashSet<>();//存储String类型的HashSet,不止可以是String可		以是其他类型,Integer 对象...
    HashSet stringHashSet = new HashSet();//存储任意类型
    

HashSet常用方法

public boolean add(E e)

  • 对集合添加元素

public boolean contains(Object o)

  • 判断集合是否包含 指定元素(对象) O.

public boolean remove(Object o)

  • 删除集合中指定元素(对象) O

public boolean isEmpty()

  • 判断集合是否为空

public void clear()

  • 删除集合中所有元素

当HashSet中的泛型为 自己写的类创建的对象时

import java.util.Objects;
//定义Student类
public class Student implements Comparable{
    private int id;
    private String name;

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

    @Override
    public String toString() {
        return id+name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id==id &&
                name.equals(student.name);
    }
//如果我们想要对象中内容相同的元素判断为重复元素,就必须在我们类中重写hashCode()与equals()方法
    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }

    @Override
    public int compareTo(Object o) {
        return this.id-((Student)o).id;
    }
}

HashSet<Student> studentHashSet = new HashSet<>();
        Student student = new Student(101, "cwy");
        Student student1 = new Student(102, "dqw");
        Student student2 = new Student(103, "wyn");
        Student student3 = new Student(101, "cwy");

        studentHashSet.add(student);
        studentHashSet.add(student1);
        studentHashSet.add(student2);
        studentHashSet.add(student3);
        System.out.println(studentHashSet);

如果Student类中没有重写hashCode()equals()方法,则运行结果会发现存在重复项,这是因为判断重复项时会调用类中的hashCode()计算hash值,类中若是没有hashCode()会调用父类的hashCode(),即Object类中的public native int hashCode();(native本地方法,由操作系统提供的)所以,只要是new出来的,调用Object值,是内存地址,肯定不同

如果我们想要对象中内容相同的元素判断为重复元素,就必须在我们类中重写hashCode()与equals()方法

对单列集合的遍历

  • 使用for循环

  • ArrayList<String> stringArrayList = new ArrayList<>();
    //1.for循环遍历
            for (int i=0;i<stringArrayList.size();i++){
                if(stringArrayList.get(i).equals("2")){
                    stringArrayList.remove(i);
                    //for循环的时候,是支持从元素中删除元素的,但是删除后,后面的元素会前移,需要控制索引
                i--;
                }
            }
            System.out.println(stringArrayList);
    //2.增强for循环    不支持删除元素
            for(String s:stringArrayList){
                System.out.println(s);
            }
    
  • 使用迭代器

  • //3.使用迭代器1.0遍历Iterator 支持删除方法,且不会漏掉元素
           Iterator<String> iterator = stringArrayList.iterator();
           while(iterator.hasNext()){
               String s = iterator.next();
               if(s.equals("2")){
                   iterator.remove();//迭代器中删除的方法
               }
               System.out.println(s);
           }
           //迭代器2.0 只能遍历List
           ListIterator<String> stringListIterator = stringArrayList.listIterator();
           while(stringListIterator.hasNext()){
               String s = stringListIterator.next();
               System.out.println(s);
           }
           //从指定位置索引
           ListIterator<String> stringListIterator2 = 			`			           stringArrayList.listIterator(stringArrayList.size());//size=最大索引+1
           //逆序
           while(stringListIterator2.hasPrevious()){
               String s = stringListIterator2.previous();
               System.out.println(s);
           }
    

双列集合

doubleCollection

HashMap(对Map接口的实现)

底层实现逻辑

HashMap结构图

转化红黑树的原理如下

补:TreeMap键值都可以是null

构造方法

  • HashMap<String,String> stringHashMap = new HashMap<>();//创建HashMap对象,键值分别是String与String  当然也可以是其他的类
    

常用方法

public V put(K key, V value)

  • 对图进行添加新的元素(必须对应你预定的类型)

  • 	stringHashMap.put("1", "一");
           stringHashMap.put("3", "三");
           stringHashMap.put("5", "五");
           stringHashMap.put("4", "四");
           stringHashMap.put("2", "二");
           stringHashMap.put("1", "一");
           stringHashMap.put(null, "一");
           //存储也是无序且不重复的,且只判断前面部分是否重复(即Key值不可以重复),
    
  • 存储原理如上面的原理图

  • 存储原理如下

public void clear()

  • 清空所有元素

public V remove(Object key)

  • 删除指定键的元素,并返回对应的值

  • System.out.println(stringHashMap.remove("1"));//删除键并返回对应值
    

public boolean containsKey(Object key)

  • 判断指定的Key键是否存在

  • System.out.println(stringHashMap.containsKey("1"));//判断键是否存在
    

public boolean containsValue(Object value)

  • 判断指定的Value是否存在

  • System.out.println(stringHashMap.containsValue("二"));//判断值是否存在
    

public boolean isEmpty()

  • 判断集合是否为空

  • System.out.println(stringHashMap.isEmpty());//判断集合是否为空
    

public int size()

  • 返回元素个数即元素长度

  • System.out.println(stringHashMap.size());//判断元素个数
    

public V get(Object key)

  • 通过Key键来返回对应的Value值

  • System.out.println(stringHashMap.get("1"));//通过Key返回Value
    

HashMap的键值遍历

  • 对值的遍历
Collection<String> collection = stringHashMap.values();//获取值 System.out.println(collection);
  • 对键的遍历
Set<String> set =stringHashMap.keySet();//获取键的集合
 System.out.println(set);

结合public V get(Object key)可以遍历对应的Value

  • 对整体的遍历(使用迭代器)
       //通过entrySet()  获取到Entry类型的集合,Entry中放有键值对
        Set<Map.Entry<String,String>>entries = stringHashMap.entrySet();
        for(Map.Entry<String,String> empty:entries){
            System.out.println(empty);
            System.out.println(empty.getKey());//获取键
            System.out.println(empty.getValue());//获取值
        }

Hashtable(底层与HashMap一样,只不过方法修饰符不同)

底层结构与HashMap相同,但是线程安全的,方法被synchronized关键字修饰

TreeMap(实现SortedMap接口)

  • 有序性:TreeMap 存储的键值对是有序的。具体来说,键的插入顺序和自然排序顺序或者自定义的比较器排序顺序是一致的
  • 其余方法都与TreeMap一样

File 类

  • File类是java.io包中很重要的一个类

  • File类的对象可以表示文件,还可以表示目录,在程序中的一个File类对象可以代表一个文件或目录

  • File对象可以对文件或目录的属性进行操作,如:文件名,最后修改日期,文件大小等

  • File对象无法操作文件的具体数据,即不能对文件进行的操作

File 构造方法

方 法 原 型 说明
File (String pathname) 指定文件(或目录)名和路径创建文件对象
//在当前目录先创建一个与aaa.txt文件名相关联的文件对象
File f1 = new File("aaa.txt");
//指明详细的路径以及文件名,请注意双斜线或用反斜杠
File f2 = new File("D:\\Java\\Hello.java");
//指明详细的路径以及目录名,请注意双斜线
File f3 = new File("D\\Java");
方法原型 说明
boolean exists() 判断文件是否存在 存在-true 不存在-false
boolean isFile() 判断是否为文件,是-true 不是-false
boolean isDirectory() 判断是否为目录,是目录-true 不是-false
String getName() 获取文件名称
long length() 获取文件长度(字节数)
boolean creatNewFile()throws IOException 创建新文件,成功-true 失败false,有可能抛出IOException异常,必须捕捉
boolean delete() 删除文件,成功-true 失败-false
public String[] list() 将目录下的子目录以及文件的名字,返回到String数组
public Flie[] listFiles() 将目录下的子目录以及文件的实例返回到File数组

输入输出(I/O)

  • 输入输出(I/O)把电脑硬盘的数据读到程序中,称为输入,即input,进行数据的read操作从程序往外部设备写数据,称为输出,即output,进行数据的write操作

体系图

IO体系图

字节流与字符流

  • 从数据流编码格式上划分为
    • 字符流
    • 字节流

输入流与输出流

  • 流按着数据的传输方向分为:

    • 输入流: 往程序中读叫输入流
    • 字节流:从程序中往外写叫输出流
    • InputStreamOutputStream的子类都是字节流,可以读写二进制文件,主要处理音频图片,歌曲,字节流,处理单元为 一个字节
    • ReaderWriter的子类都是字符流 主要处理字符或字符串,字符流处理的单元为 一个字符. 字节流将读取到的字节数据,去指定的编码表中获取对应文字字符
  • 字节流中常用的类

    • 字节输入流 FIleInputStream
    • 字节输出流FileOutputStream
  • 字符流的常用类

    • 字符输入流 FileReader
    • 字符输出流FileWriter

输入输出字节流

构造方法

FileInputStream in =new FileInputStream("D:\\777\\新建 文本文档.txt");//括号里面也可以是File对象
FileOutputStream out = new FileOutputStream("D:\\aaaa.c");////括号里面也可以是File对象

方法

InputStream 基本方法

public int read() throws IOException

  • 读取一个字节并以整数的形式返回(0~255),如果返回-1,已到输入流的末尾

public int read(byte[] buffer) throws IOException

  • 读取一系列字节并存储到一个数组buffer,返回实际读取的字节数,如果读取前已到输入流的末尾,返回-1

public void close() throws IOException

  • 关闭流,释放内存资源

OutputStream 基本方法

public void write(int b) throws IOException

  • 向输出写入一个字节数据,该字节数据位参数b的低8位

public void write(byte[] b,int off,int len) throws IOException

  • 将一个字节类型数组中的从指定位置(off)开始到len个字节写入输出流

public void close() throws IOException

  • 关闭流释放内存资源

实际使用

public class StreamDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream in =new FileInputStream("D:\\777\\新建 文本文档.txt");
        FileOutputStream out = new 			 FileOutputStream("D:\\aaaa.c");
//        System.out.println(in.read()); //每read一次返回一个字节编码 读完返回-1
//
//        System.out.println(in.read());
/*
    int read() 默认一次读一个字节,返回的是读到的字节编码 返回效率低
    int read(byte[] b) 默认一次读一个指定大小的byte个字节 返回的数组中一次实际
                    装入的字节个数
 */
        int t;
        while((t=in.read())!=-1){
            out.write(t);
        }
        int size;
        byte[] b = new byte[10000];
        while((size=in.read(b))!=-1){
            out.write(b,0,size);//从0开始读,读到文件字节长度,比直接write(b)节省空间

        }
        //关闭流对文件的占用
        in.close();
        out.close();
    }
}

  • DataOutputStreamDataInputStream 也属于输入输出字节流,与FIleOutputStream,FileInputStream区别:前者的输入输出是以原始数据类型为基本单位输入输出(char ,int ),后者则是以 字节 为单位进行读写

  • DataOutputStreamDataInputStream底层还是对字节的处理

输入输出字符流(Reader Writer)

构造方法

File file =new File("D:/777/888/999/新建 文本文档.txt");
Reader read = new FileReader(file);
//....还有其他多种构造对象方法
 Writer writer = new FileWriter(file);

方法

方法与上面方法名与用法一样,区别:Reader与Writer只能处理纯字符的文件!!!!

Print流

  • Print 打印流
    • 只做输出没有输入 打印流分为字节打印流和字符打印流
    • PrintWrite
      • 字符打印流 print 方法可以打印各种类型数据
      • 在javaweb 项目中,服务器端相应数据以打印流的方式响应

对象输入输出流--对象序列化

序言:对象的寿命通常随着生成该对象的程序终止而终止,有时候,可能需要将对象的状态保存下来,在需要时候再将对象恢复

对象的序列化可以形象地比喻为将一个对象从内存中“拍成照片”,并将其存储在可以持久化的存储设备(如磁盘)上。当需要还原这些数据时,可以通过反序列化的过程,将这些照片重新“冲洗”出来,并还原成原始对象

  • 对象的输入输出流
  • 主要作用:用于写入对象信息与读取对象信息,对象信息一旦写到文件上那么对象的信息就可以做到持久化
    • 对象输出流ObjectOutputStream
    • 对象输入流ObjectInputStream
    • 当然也有对应的读写方法
      • readObject()方法读取一个对象
      • writeObject方法将对象保存到输出流
  • 对象的输出流将制定的对象写入到文件的过程中,就是将对象序列化的过程
  • 对象的输入流将指定序列化好的文件读出来的过程,就是将对象反序列化
  • 对象的输出流将对象写入到文件中称为对象的序列化, 所以被序列化的对象必须要实现 Serializable接口,Serializable接口中没有任何方法.  当一个类声明实现Serializable接口后,表明该类可被序列化

private static final long serialVersionUID = -5974713180104013488L

  • 随机生成 唯一serialVersionUID用来表明实现序列化类的不同版本间的兼容性. 某个类在与之对应的对象已经序列化出去后做了修改,该对象依然可以被正确的反序列化
  • 如果不显示生成序列号,那么将会隐式生产,但隐式生成后,类一旦发生改变,序列号也会随之变化

transient 关键字

  • 默认情况下当执行了对象序列化的时候会将类中的全部属性的内容进行全部的序列化操作但是很多情况下有一些属性并不需要序列化处理,这个时候就可以在属性的定义上使用transient关键字;来完成了
  • private transient String name;

idea中设置 ,在类中生成序列化id

image-20231019221304773

image-20231019221323832

异常

异常体系结构

1697807548978

异常处理机制

  • 默认处理机制

    • java中默认的异常处理机制:
      当程序出现异常后,会将异常包装在一个对应的对象中,并抛出此对象.
      并终止程序的运行.
  • 手动处理

    • Java中提供一套异常处理机制,在程序发生异常时,可以执行预先设定好的处理程序,执行完成后,程序不会停止,可以继续向后执行.
  • 异常处理:
    1.遇到异常就终止程序运行(不想要的)
    2.遇到程序异常时,进行处理

在写代码的时候,就要根据不同的情况设定好处理程序

运行程序

  • 如果程序执行时,出现问题,执行异常处理程序

  • 如果程序执行时,没有出现问题,不需要执行异常处理程序

注意: 语法错误 并非异常

  • int a = 10 后面忘加了分号,程序写错了

常见的几种异常

NullPointerException 当应用程序试图在需要对象的地方使用 null 时,会抛出此异常。例如,调用空对象的方法或访问空对象的成员变量。
IndexOutOfBoundsException 此异常通常发生在尝试访问数组、字符串或其他类型的集合时,索引超出了其实际范围
ArithmeticException 当出现异常的算术条件时,例如除以零,会抛出此异常。
ClassCastException 当尝试将对象强制转换为不是实例的子类时,会抛出此异常。
llegalArgumentException 当向方法传递了一个非法或不适当的参数时,会抛出此异常。
ConcurrentModificationException 当程序在迭代一个集合的同时,另一个线程试图修改这个集合,就会抛出这个异常
FileNotFoundException 当试图打开一个不存在的文件时,会抛出此异常
IOException 这是一个一般性的I/O错误,通常用于指示输入/输出操作失败或中断的异常。
ClassNotFoundException JVM试图通过其字符串名称加载类,但在已加载的类中找不到相应的类定义时,会抛出此异常
ArrayIndexOutOfBoundsException 当应用程序试图访问数组的非法索引时(即负数或大于等于数组大小的数)时,会抛出此异常。
NullPointerException 当一个应用程序试图在需要对象的地方使用 null 时,会抛出此异常。
NumberFormatException 当一个应用程序试图在需要对象的地方使用 null 时,会抛出此异常。
InterruptedException 当线程正在等待、休眠或占用,并且线程的中断优先级被设置为InterruptedException时,会抛出此异常。

异常处理中的几个常用关键字(try catch finally throw throws)

异常处理
   java中提供一套异常处理机制,在程序发生异常时,可以执行预先设定好的处理程序,
   执行完成后,程序不会停止,可以继续向后执行.
   
   在写代码的时候,就要根据不同的情况设定好处理程序,
   运行程序
   如果程序执行时,出现问题,执行异常处理程序
   如果程序执行时,没有出现问题, 不需要执行异常处理程序
   
   关键字
     try{
	    写代码,可能会出现异常
	 }catch(异常类型 a){ 捕获指定类型的异常
	    
	 }finally{
	     最终必须要执行的代码
	 }
	try{
            int[] a = {1,2,3};
            int b = a[4];
        }catch (ArrayIndexOutOfBoundsException a){
            a.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("666");
    }

注:如果出现多个错误,则会抛出共同的父类

throw 与 throws区别

在Java中,throwsthrow是两个不同的关键字,它们在异常处理中起着不同的作用。

  1. throws关键字:
    • throws用于声明一个方法可能会抛出的异常。当一个方法可能会抛出异常时,我们需要使用throws关键字在方法签名中声明这些异常。这样,调用该方法的代码就需要处理这些异常,或者继续向上抛出。
    • 例如,如果一个方法可能会抛出IOException,我们可以这样声明:public void readFile() throws IOException
  2. throw关键字:
    • throw用于手动抛出异常。我们可以使用throw关键字在代码中直接抛出一个异常对象。
    • 例如,我们可以这样手动抛出一个IOExceptionthrow new IOException("File not found");

总结一下,throws用于声明方法可能抛出的异常,而throw用于手动抛出异常。这两个关键字在异常处理中是相辅相成的,它们一起帮助我们处理和传递异常。

编译期异常和运行期异常的区别

编译期异常和运行期异常的区别如下

  1. 异常处理要求不同:编译期异常(也称为检测异常checked Exception)要求在代码中显式地处理(使用try-catch或者throws)。运行时异常(也称为unchecked Exception)不要求显式地处理。
  2. 异常检测时机不同:编译期异常在代码编译阶段就可以被检测到。运行时异常在代码运行过程中才能被检测到。
  3. 异常必要性不同:编译期异常通常表示外部环境或者业务逻辑上的问题,需要通过异常处理来解决。运行时异常通常表示程序中的错误或者编程错误,可以通过代码改进避免

自定义异常

JavaAPI中定义的标准异常类,都是与语法有关的(例如索引越界,空指针......),

但是我们的程序有可能不满足某种业务条件时,想以抛出异常的形式处理,此时就需要自定义一个与

业务相关的异常类来表示(如 分数不合法,提供scoreException);

  • 在Java中,您可以通过继承ExceptionRuntimeException类来创建自定义异常。这些异常可以具有您需要的任何特定格式。以下是一个创建自定义异常的简单示例

  • // 创建一个自定义异常类
    class MyException extends Exception {
        public MyException(String message) {
            super(message);
        }
    }
    
    // 在代码中抛出这个异常
    public class Test {
        public static void main(String[] args) {
            try {
                throwException();
            } catch (MyException e) {
                e.printStackTrace();
            }
        }
    
        public static void throwException() throws MyException {
            throw new MyException("这是我的异常");
        }
    }
    

    我们可以写一个分数评估的功能,如果出现非法输入可以抛出一个异常

    //自定义异常
    public class ScoreException  extends Exception{
        public ScoreException(){
    
        }
        public ScoreException(String message){
            super(message);//直接调用父类的构造方法
        }
    }
    
    package day14;
    
    public class TestScoreException {
        public static void main(String[] args) {
            try {
                System.out.println(scoreClass(1000));
            } catch (ScoreException e) {
                e.printStackTrace();
            }
        }
        public static char scoreClass(int score) throws ScoreException {
            if(score<0||score>100){
                throw new ScoreException("输入非法分数");
            }
            if(score>=90){
                return 'A';
            }else if(score>=80){
                return 'B';
            }else if(score>=70){
                return 'C';
            }else{
                return 'D';
            }
        }
    }
    

    image-20231028155759869

网络编程

什么是计算机网络?

  • 把分布在不同地理区域的具有独立功能的计算机,通过通信设备与线路连接起来,由功能完善的软件实现资源共享和信息传递的系统

简单来说就是把不同地区的计算机通过设备连接起来,实现不同地区之前的数据传输

网络编程是干什么的?

  • 网络编程是借助计算机网络,实现我们所写的程序,在不同电脑上,可进行数据的传输

  • java是支持网络间的数据传输的,降低层细节封装起来了,给程序员提供了一套标准的类库很方便使用java语言开发可以进行网络通信的软件

  • 网络编程的核心问题

    • ​ 如何找到网络世界中的恶目标主机,以及目标软件

    • ​ 在终端 使用 ipconfig 指令查看ip

    • ​ 如何安全可靠的进行数据传输 协议 规则

网络的一些基本知识

  • 网络模型
  • ​ OSI参考模型 是一个理想化的标准模型
    • ​ 分成七层
  • ​ TCP/IP参考模型
    • ​ 分成四层
    • ​ 应用层(http)
    • ​ 运输层(协议)
    • ​ 网络层(ip)
    • ​ 物理链路层(硬件设备)

如下:

image-20231022180449559

image-20231022180529233

  • 通信要素 ip 端口 协议
    • IP:在网络世界中,是计算机的地址
    • 局域网地址: 192.168.1.20 连接到路由器,会自动分配IP
    • 广域网地址: 家里的宽带 与外界连接
    • 本机地址: 本地回环地址 127.0.0.1
    • 端口:计算机中运行中的程序的编号,对应的是程序
      • 端口号0-65535之间 由于0-1024被一些系统程序使用,所以我们开发的程序可以从
        1024-655335区设定端口,但是不能与已有的发生冲突
    • ip+端口 找到目标计算机 以及你想要的程序

image-20231022180547107

如何进行安全信息传输--传输协议

  • Java中分为两种编程协议
    • TCP
    • UDP

TCP协议通信原理

先检测网络是否通畅,客户端是否能连接到服务器端
如果能连接到,则进行数据的传输,如果连接不到,就会报错
采用3次握手的机制 (连接请求)
1.第一次客户端给服务器发送一个信息
2.服务器收到客户端的请求后,需要给客户端做出一个反馈(表示服务器端收到客户端消息)
3.客户端收到服务器端确认反馈后,再一次向服务器发送一个反馈,以确保服务器知道他的
反馈,客户端是收到 (表示客户端成功收到服务器端消息)

TCP是可靠的安全的,相对于UDP效率低

image-20231023224832805

四次挥手(端来请求)

  • 1.客户端 向服务器端发送一个断开请求
  • 2.服务器端 向客服发出一个反馈
  • 3.服务器端把没有发完的数据全部发送
  • 4.客户端再向服务器端发送最终断开的信号

image-20231023225803452

UDP协议通信原理

  • ​ 将数据分装成一个一个数据报
  • ​ 包含 数据 源(自己电脑ip) 目标(接受ip 端口)
  • ​ 只管发送 是否成功,不知道
  • ​ 是不安全的,但是效率高

不用建立连接,直接发送

image-20231207193831271

TCP编程

服务端

ServerSocket常用的方法

  • Socket accept() throws IOException
    • 等待客户端的连接请求,返回与该客户端进行通信的Socket对象
  • void close() throws IOException
    • 关闭监听Socket
package day15;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*
服务器端
 */
public class Server {
    public static void main(String[] args) {
        //创建并启动服务器
        try{
            ServerSocket serverSocket = new ServerSocket(9999);
            System.out.println("服务器启动成功");

            while(true) {
                Socket socket = serverSocket.accept();//监听有没有客户端连接到服务器,监听时,会阻塞程序
                System.out.println("有傻逼端连接到服务器");
                //接收客户端发送的数据
                InputStream inputStream = socket.getInputStream();
                //下面过于复杂,可以调用DataInputStream  但是必须对应DataOutputStream
//                byte[] bytes = new byte[100];
//                int size = inputStream.read(bytes);
//                String s = new String(bytes, 0, size);
//                System.out.println(s);
                DataInputStream dataInputStream = new DataInputStream(inputStream);
                String s= dataInputStream.readUTF();
                System.out.println(s);


                //服务器向客户端发送消息
                OutputStream outputStream =socket.getOutputStream();
                DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
                dataOutputStream.writeUTF("已经收到");

                // 确保数据被发送出去,并释放资源
                dataOutputStream.flush();
                dataOutputStream.close();
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("服务器启动失败,端口被占用");
        }
    }
}

客户端

常用方法

  • void close() throws IOException
    • 关闭Socket 不可以在以后得网络连接中使用 除非创建新的套接字
  • InputStream getInputStream() throws IOException
    • 获取与Socket相关联的字节输入流,用于从Socket中读入数据
  • OutputStream getOutputStream() throws IOException
    • 获取与Socket相关联的字节输出流 ,用于向Socket中写入数据
package day15;
import java.io.*;
import java.net.Socket;
/*
客户端
 */
public class Client {
    public static void main(String[] args)  {

        //创建客户端
        //127.0.0.1自己地址
       try {

               //向服务器发送
               Socket socket = new Socket("127.0.0.1", 9999);
               //在客户端向服务器端发送一句话
               OutputStream outputStream = socket.getOutputStream();//获取输出流
               DataOutputStream dataOutputStream =new DataOutputStream(outputStream);

               String s = "你好";
               dataOutputStream.writeUTF(s);

               //接受服务器
               InputStream inputStream = socket.getInputStream();
               DataInputStream dataInputStream = new DataInputStream(inputStream);
               System.out.println(dataInputStream.readUTF());


       } catch (IOException e) {
           e.printStackTrace();
           System.out.println("连接服务器失败");
       }
    }
}

UDP编程

服务端

package day16;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UDPReceive {
    public static void main(String[] args) {
        try {
            while(true){
                //接受数据的对象
                DatagramSocket datagramSocket = new DatagramSocket(9999);

                byte[] bytes = new byte[100];
                //接受数据包
                DatagramPacket datagramPacket = new DatagramPacket(bytes,0, bytes.length);
                datagramSocket.receive(datagramPacket);
                String s = new String(bytes,0,datagramPacket.getLength());
                System.out.println(s);
                datagramSocket.close();
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

客户端

package day16;

import java.io.IOException;
import java.net.*;

//发送端
public class UDPSendDemo {

    public static void main(String[] args) {
        try {
            DatagramSocket datagramSocket = new DatagramSocket();//负责发送数据报
            byte[] bytes = "你好sb邓钦文".getBytes();
            DatagramPacket datagramPacket = new DatagramPacket(bytes,0, bytes.length, InetAddress.getByName("127.0.0.1"),9999);

            //发送(datagramSocket方法)
            datagramSocket.send(datagramPacket);
            datagramSocket.close();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

TCP 与 UDP的区别

TCP(传输控制协议)和UDP(用户数据报协议)都是网络传输层协议,用于在网络中传输数据。它们之间的主要区别在于连接方式可靠性数据流方式功能

  1. 连接方式
    • TCP是面向连接的协议,发送方和接收方在发送数据之前,必须通过三次握手建立连接。这个过程确保了双方都已准备好进行数据传输。
    • UDP是无连接的协议,发送方在向接收方发送数据时不需要建立连接。这意味着UDP可以更快地发送数据,但也可能导致数据的丢失或乱序。
  2. 可靠性
    • TCP通过序号机制、确认机制、超时重传机制和数据校验来保证传输的可靠性。如果数据在传输过程中丢失或损坏,TCP会重新发送数据,直到接收方成功接收为止。
    • UDP只添加了端口和差错检查的功能,不提供数据的可靠性保证。因此,UDP在传输过程中可能会出现数据丢失或乱序的情况。
  3. 数据流方式
    • TCP是面向字节流的协议,将应用层传递下来的数据当做无结构的数据流进行处理。TCP不知道所传数据的具体含义,只是将数据块拼接成一个段进行发送。
    • UDP是面向报文的协议,发送方的UDP对应用程序交下来的报文添加首部后直接向下交付IP层。每个报文都是独立发送的,不需要进行拼接。
  4. 功能
    • TCP支持单播、多播和广播的功能,可以实现一对一、一对多、多对多和多对一的数据传输。
    • UDP同样支持这些传输方式,但由于其无连接的特性,使得UDP在实时性要求较高的场景中更为适用,如视频流、音频流等。

tcp_udp1

线程

概念

1.程序:为解决某种问题,使用计算机语言编写的一系列指令(代码)的集合

2.进程:正在运行的程序(被加载到内存中),是操作系统进行资源分配的最小单位

3.线程:进程可以进一步细化为线程(比进程更小)且线程是隶属于进程的,是操作系统执行的最小的执行单元 也是cpu进行任务调度的最小单位

  • 如 :运行的QQ也是一个进程,操作系统就会为这个进程分配资源 一个聊天窗口就是一个线程,线程隶属于进程

tips:早期是没有线程的,是以进程为单位执行的,进程的单位比较大,当一个进程运行时,其他进程就不能执行了,所以后来,将进程中的多个任务细化为线程,cpu的执行单位,也是从进程进化为更小的线程

总结

  1. 一个进程可以包含多个线程
  2. 一个线程只能隶属于一个进程,线程不能脱离进程单独独立运行
  3. 一个进程中至少有一个线程,即主线程,javamian方法就是用来启动主线程
  4. 在主线程中可以创建并启动其他线程,所有线程都共享进程的内存资源
  5. 所有线程都共享进程资源

Thread类

JavaThread表示线程,提供了许多的方法,来对线程的控制,可以通过继承Thread 类来实现线程

Thread常用方法

  • Thread.currentThread();
    • 获取当前运行的线程
  • run();
    • 线程要执行的任务
  • start();
    • 启动Java线程
  • setName(String name);
    • 设置线程名字
  • (String)getName();
    • 获取线程名字
  • getPriority();
    • 获取线程优先级
  • setPriority();
    • 设置线程优先级 1~10一般线程默认优先 5
  • sleep(long ms);
    • 设置线程休眠
  • join();
    • 让其他线程等待这个线程结束后,其他线程再执行

Thread构造方法

  • new Thread(Runnable runnable);
    • 接受一个任务对象
  • new Thread(Runnable runnable,String name);
    • 接受一个对象 并为对象设置名字

使用上面方法进行实例

线程1
public class MyThread extends Thread{

    //重写run方法,在run中执行我们要执行的方法
    @Override
    public void run() {
        for(int i=0;i<10000;i++){
            System.out.println("循环main"+i);
        }
    }

}
线程2
public class MyThread2 extends Thread {
    @Override
    public void run() {
        for(int i=0;i<10000;i++){
            System.out.println("循环main2"+i);
        }
    }
}
public class 线程 {
    public static void main(String[] args) {
        for(int i=0;i<10000;i++){
            System.out.println("循环1 "+i);
        }
        for(int i=0;i<10000;i++){
            System.out.println("循环2 "+i);
        }
        for(int i=0;i<10000;i++){
            System.out.println("循环3 "+i);
        }
        for(int i=0;i<10000;i++){
            System.out.println("循环4 "+i);
        }
        /*
        这样不管怎么执行都是单线程,从上到下依次执行,

        所以如果想在java程序中有几件不想管的事件同时执行
         可以在java中创建线程,把一些需要执行的任务放在线程中执行,
         这样就拥有让cup执行的权利
         */
        MyThread myThread = new MyThread();
        //调用start方法让程序多线程进行,重写run方法但不能调用run,不然只是普通方法的调用,依然是单线程
        myThread.start();
        MyThread2 myThread2 = new MyThread2();
        myThread2.start();

    }

}

image-20231207203645612

观察结果 可知 MyThreadMyThread2是同时进行的

Runnable接口

Java中我们也可以实现Runnable接口来进行对线程的实现

思路.

  • 创建一个类 实现Runnable接口 重写run方法

  • 在main函数使用Thread构造方法传入Runnable对象

  • public class MyTask implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<10;i++){
                System.out.println("啊");
            }
        }
    }
    
  • MyTask myTask = new MyTask();
           //创建一个线程
           Thread thread = new Thread(myTask);
    

Callable接口

Java中也可以实现Callable接口 重写 call()方法 来对线程的实现

思路.

  • 重写call()方法 call()方法有返回值 可以抛出异常,还可以处理不同类型
  • 依然使用Thread构造方法来创建一个线程

关于线程的其他概念

线程生命周期

从线程到销毁期间经历五个状态

image-20231029190539052

守护线程(也是线程的一种)

  • setDaemon();如果一个线程守护线程,那么它会等待java中其他线程任务结束后,自动终止
  • 守护线程是为其他线程提供服务的,eg:jvm中的垃圾回收机制,就是一个守护线程

多线程

在一个应用程序中,存在多个线程,不同线程可以并行的执行任务

操作系统线程任务调度算法

  1. 先来先服务调度算法
  2. 短作业优先调度算法
  3. 优先级调度算法
  4. 高响应比优先调度算法
  5. 时间片轮转调度算法
  6. 多级反馈队列调度算法(集合前几种算法的优点)

对线程进行加锁

分析

  • 多线程的优点
    • 提高程序的处理能力
    • 提高CPU的 利用率
  • 缺点
    • 线程也是需要占用内存资源和CPU资源
    • 多个线程对同一个共享资源进行访问,会出现线程安全问题

对于内存资源与CPU资源我们可以通过不断增加内存来解决

对于线程安全问题 我们则需要对 线程进行加锁

synchronized

synchronized 修饰代码块需要加一个同步对象 synchronized(同步对象)

同步对象要求

  • 多线程用到的对象必须是同一个对象
  • 可以是java类中任何类对象.
  • 作用:用来记录有没有线程进入到同步代码块中

修饰方法

  • 锁不需要我们提供,会默认提供锁对象
  • synchronized如果修饰的是非静态方法,锁的对象是this
  • 如果是静态方法,锁的对象是Class的对象 (一个类只能有一个对象)

修饰代码块例子

package day17;

public class PrintNums implements Runnable{
    //实现线程的交替打印
    int num=100;
    Object object = new Object();
    @Override
    public void run() {
        while(true){
            synchronized (object){
                object.notify();//唤醒线程
                if(num==0){
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"."+num+".");
                num--;
                try {
                    object.wait();//线程等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
package day17;

public class TestPrintNums {
    public static void main(String[] args) {
        PrintNums printNums = new PrintNums();
        Thread t1 = new Thread(printNums,"1");
        Thread t2 = new Thread(printNums,"2");
        Thread t3 = new Thread(printNums,"3");
        t1.start();
        t2.start();
        t3.start();
    }
}

image-20231207211850022

于是我们实现了交替打印 每次只能进入一个线程

修饰方法(同步对象会有默认的)

  1. 锁不需要我们提供,会默认提供对象锁
  2. synchronized如果修饰的是非静态方法,锁的对象是this

synchronized如果修饰的是静态方法,锁的对象是类的Class对象,一个类只有 一个对象

  • 如果是非静态方法,那么同步对象是this(如果是非静态对象,我们可以使用Runnable创建线程方法)

  • 如果是静态方法,所得对象是当前类的Class对象

  • 当前类的Class对象:一个类加载到内存后会为这个类创建唯一的Class类的对象

  • Class类实例表示正在运行的Java应用程序中的类和接口

  • 修饰静态方法
    public class SellTicket extends Thread {
        static int counts = 10;//10张票
        static Object object = new Object();
    
        @Override
        public void run() {
    
            while (counts > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sellTicket();
            }
        }
    
        public static synchronized void sellTicket() {
            if(counts>0){
                System.out.println(Thread.currentThread().getName()+"买了"+counts+"张票");
                --counts;
            }
        }
    }
    public class Test {
        public static void main(String[] args) {
            SellTicket t1 = new SellTicket();
            t1.setName("窗口1");
            SellTicket t2 = new SellTicket();
            t2.setName("窗口2");
    
            t1.start();
            t2.start();
        }
    }
    

    image-20231209231353289

  • 修饰非静态方法
    public class SellTickets2 implements Runnable{
    
        int t=20;//票数
    
        @Override
        public void run() {
            while(t>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sell2();
            }
        }
        public synchronized void sell2(){
            if(t>0){
                System.out.println(Thread.currentThread().getName()+"买了"+t+"张票");
                --t;
            }
        }
    }
    
    public class Test2 {
        public static void main(String[] args) {
            SellTickets2 sellTickets2 = new SellTickets2();
            Thread t1 = new Thread(sellTickets2,"窗口1");
            Thread t2 = new Thread(sellTickets2,"窗口2");
    
            t1.start();
            t2.start();
        }
    }
    

    image-20231209231446179

ReentrantLock类加锁

  • 只能对某一代码块进行加锁,不能对整个方法加锁
  • 加锁方法如下
ReentrantLock r = new ReentrantLock();//创建 ReentrantLock对象
r.lock();<---------|//加锁
                   |
  .............    |------被锁部分(一次只进去一个线程)
                   |
r.unlock();<-------|//解锁

tips:r.unlock();最好写在finally{};代码块中,保证发生异常时,也能够解锁;

synchronized与ReentrantLock的区别

  1. synchronized是一个关键字,ReentrantLock是一个类

  2. synchronized修饰代码块和方法,ReentrantLock只能修饰代码块

  3. synchronized可以隐式的加锁和释放锁,运行出现异常可以自动释放锁

    ReentrantLock需要手动加锁和释放锁,建议在finally代码中释放锁

常用的三个方法 wait notify notifyAll

  • wait();方法使当前线程进入等待状态,直到另一个线程调用该对象的notify()notifyAll()方法来唤醒它

  • notify();方法唤醒在该对象上调用wait()方法进入等待状态的一个线程,如果有多个线程在等待,则只会唤醒其中一个线程。

  • notifyAll();方法唤醒在该对象上调用wait()方法进入等待状态的所有线程。

    tips:1.都是Object类中定义的方法

    ​ 2.三个方法必须在同步代码块中使用

    ​ 3.这三个方法必须通过为锁的对象调用

Java GUI

1、Java GUI 概述

GUI(Graphical User Interface,简称 GUI,图形用户界面)是指采用图形方式显示的计算机操作用户界面,与早期计算机使用的命令行界面相比,图形界面对于用户来说在视觉上更易于接受。

Java GUI主要有两个核心库,分别是AWT(java.awt:Abstract Windows ToolKit(抽象窗口工具包))和Swing(javax.swing:AWT的扩展),AWT需要调用本地系统方法来实现功能,属重量级控件,而Swing是在AWT的基础上,建立的一套图像界面系统,其中提供了更多的组件,而且完全由Java实现,增强了移植性,属轻量级组件。

2、容器

容器(Container)是组件(Component)的子类,一个容器可以容纳多个组件,并使他们成为一个整体。容器可以简化图形化界面的设计,以整体结构来布置界面,所有的组件都可以通过add()方法加入容器中。容器共有四种类型,分别是窗口(JFrame)、弹窗(JDialog)、面板(JPanel)、滚动面板(JScrollPanel)。

2、1 窗口

Frame或JFrame类用于创建一个具有标题栏的框架窗口作为程序的主要界面,它不依赖其他容器可以单独存在。

public class JFrameUse {
    public static void main(String[] args) {
        // 初始化窗口
        JFrame jFrame = new JFrame("这个是窗口的标题");
        // 设置窗口的位置和大小
        jFrame.setBounds(400, 300, 500, 500);
        // 设置窗口的背景颜色
        jFrame.setBackground(new Color(175, 114, 114));
        // 设置窗口是否可见
        jFrame.setVisible(true);
        // 设置窗口是否可以缩放
        jFrame.setResizable(false);
        /**
         * 设置窗口的相对位置。
         * 如果 comp 整个显示区域在屏幕内, 则将窗口放置到 comp 的中心;
         * 如果 comp 显示区域有部分不在屏幕内, 则将该窗口放置在最接近 comp 中心的一侧;
         * comp 为 null, 表示将窗口放置到屏幕中心。
         */
        jFrame.setLocationRelativeTo(null);
        /**
         * 设置窗口关闭按钮点击后的默认操作, 参考值:
         *     WindowConstants.DO_NOTHING_ON_CLOSE: 不执行任何操作。
         *     WindowConstants.HIDE_ON_CLOSE: 隐藏窗口(不会结束进程), 再次调用 setVisible(true) 将再次显示。
         *     WindowConstants.DISPOSE_ON_CLOSE: 销毁窗口, 如果所有可显示的窗口都被 DISPOSE, 则可能会自动结束进程。
         *     WindowConstants.EXIT_ON_CLOSE: 退出进程。
         */
        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }
}

2、2 弹窗和对话框

JDialog,对话框,使用 JDialog 类可以创建自定义有的对话框,或者调用 JOptionPane 中的多个静态方法快速创建各种标准的对话框。

JOptionPane是JavaSwing内部已实现好的,以静态方法的形式提供调用,能够快速方便的弹出要求用户提供值或向其发出通知的标准对话框。主要具有以下几种那类型:

  • showMessageDialog:消息对话框,向用户展示一个消息,没有返回值。
  • showConfirmDialog:确认对话框,询问一个问题是否执行。
  • showInputDialog:输入对话框,要求用户提供某些输入。
  • showOptionDialog:选项对话框,上述三项的大统一,自定义按钮文本,询问用户需要点击哪个按钮。

上述四个类型的方法(包括其若干重载)的参数遵循一致的模式,下面介绍各参数的含义:

  • parentComponent: 对话框的父级组件,决定对话框显示的位置,对话框的显示会尽量紧靠组件的中心,如果传 null,则显示在屏幕的中心。

  • title: 对话框标题。

  • message: 消息内容。

  • optionType: 选项按钮的类型。

  • selectionValues、initialSelectionValue: 提供的输入选项,以及默认选中的选项。

  • icon: 自定义的对话框图标,如果传 null,则图标类型由 messageType 决定。

  • messageType: 消息类型,主要是提供默认的对话框图标。可能的值为:

    • JOptionPane.PLAIN_MESSAGE 简单消息(不使用图标)
    • JOptionPane.INFORMATION_MESSAGE 信息消息(默认)
    • JOptionPane.QUESTION_MESSAGE 问题消息
    • JOptionPane.WARNING_MESSAGE 警告消息
    • JOptionPane.ERROR_MESSAGE 错误消息

对话框

class JOptionPaneUse {
    public JOptionPaneUse() {
        final JFrame jf = new JFrame("测试窗口");
        jf.setSize(400, 400);
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        /*
         * 1. 消息对话框(信息消息)
         */
        JButton btn01 = new JButton("showMessageDialog(信息消息)");
        btn01.addActionListener(e -> {
            // 消息对话框无返回, 仅做通知作用
            JOptionPane.showMessageDialog(jf, "通知信息", "消息标题", JOptionPane.INFORMATION_MESSAGE
            );
        });

        /*
         * 2. 消息对话框(警告消息)
         */
        JButton btn02 = new JButton("showMessageDialog(警告消息)");
        btn02.addActionListener(e -> {
            // 消息对话框无返回, 仅做通知作用
            JOptionPane.showMessageDialog(jf, "警告信息", "消息标题", JOptionPane.WARNING_MESSAGE);
        });

        /*
         * 3. 确认对话框
         */
        JButton btn03 = new JButton("showConfirmDialog");
        btn03.addActionListener(e -> {
            /*
             * 返回用户点击的选项, 值为下面三者之一:
             *     是:   JOptionPane.YES_OPTION
             *     否:   JOptionPane.NO_OPTION
             *     取消: JOptionPane.CANCEL_OPTION
             *     关闭: JOptionPane.CLOSED_OPTION
             */
            int result = JOptionPane.showConfirmDialog(jf, "确认删除?", "提示", JOptionPane.YES_NO_CANCEL_OPTION);
            System.out.println("选择结果: " + result);
        });

        /*
         * 4. 输入对话框(文本框输入)
         */
        JButton btn04 = new JButton("showInputDialog(文本框输入)");
        btn04.addActionListener(e -> {
            // 显示输入对话框, 返回输入的内容
            String inputContent = JOptionPane.showInputDialog(jf, "输入你的名字:", "默认内容");
            System.out.println("输入的内容: " + inputContent);
        });

        /*
         * 5. 输入对话框(下拉框选择)
         */
        JButton btn05 = new JButton("showInputDialog(下拉框选择)");
        btn05.addActionListener(e -> {
            Object[] selectionValues = new Object[]{"香蕉", "雪梨", "苹果"};
            // 显示输入对话框, 返回选择的内容, 点击取消或关闭, 则返回null
            Object inputContent = JOptionPane.showInputDialog(jf, "选择一项: ", "标题",
                    JOptionPane.PLAIN_MESSAGE, null, selectionValues, selectionValues[0]);
            System.out.println("输入的内容: " + inputContent);
        });

        /*
         * 6. 选项对话框
         */
        JButton btn06 = new JButton("showOptionDialog");
        btn06.addActionListener(e -> {
            // 选项按钮
            Object[] options = new Object[]{"香蕉", "雪梨", "苹果"};
            // 显示选项对话框, 返回选择的选项索引, 点击关闭按钮返回-1
            int optionSelected = JOptionPane.showOptionDialog(jf, "请点击一个按钮选择一项", "对话框标题",
                    JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.ERROR_MESSAGE, null,
                    options,    // 如果传null, 则按钮为 optionType 类型所表示的按钮(也就是确认对话框)
                    options[0]
            );
            if (optionSelected >= 0) {
                System.out.println("点击的按钮: " + options[optionSelected]);
            }
        });

        // 垂直排列按钮
        Box vBox = Box.createVerticalBox();
        vBox.add(btn01);
        vBox.add(btn02);
        vBox.add(btn03);
        vBox.add(btn04);
        vBox.add(btn05);
        vBox.add(btn06);

        JPanel panel = new JPanel();
        panel.add(vBox);

        jf.setContentPane(panel);
        jf.setVisible(true);
    }
}

自定义弹窗

class JDialogUse {
    public JDialogUse() {
        final JFrame jf = new JFrame("测试窗口");
        jf.setSize(300, 300);
        jf.setLocationRelativeTo(null);
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JButton btn = new JButton("显示自定义对话框");
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                showCustomDialog(jf, jf);
            }
        });

        JPanel panel = new JPanel();
        panel.add(btn);

        jf.setContentPane(panel);
        jf.setVisible(true);
    }

    /**
     * 显示一个自定义的对话框
     *
     * @param owner           对话框的拥有者
     * @param parentComponent 对话框的父级组件
     */
    private static void showCustomDialog(Frame owner, Component parentComponent) {
        // 创建一个模态对话框
        final JDialog dialog = new JDialog(owner, "提示", true);
        // 设置对话框的宽高
        dialog.setSize(250, 150);
        // 设置对话框大小不可改变
        dialog.setResizable(false);
        // 设置对话框相对显示的位置
        dialog.setLocationRelativeTo(parentComponent);

        // 创建一个标签显示消息内容
        JLabel messageLabel = new JLabel("对话框消息内容");

        // 创建一个按钮用于关闭对话框
        JButton okBtn = new JButton("确定");
        okBtn.addActionListener(e -> {
            // 关闭对话框
            dialog.dispose();
        });

        // 创建对话框的内容面板, 在面板内可以根据自己的需要添加任何组件并做任意是布局
        JPanel panel = new JPanel();

        // 添加组件到面板
        panel.add(messageLabel);
        panel.add(okBtn);

        // 设置对话框的内容面板
        dialog.setContentPane(panel);
        // 显示对话框
        dialog.setVisible(true);
    }
}

2、3 面板

面板也是一个容器,但是它不能单独存在,只能存在于窗口中,一个面板对象代表了一个长方形的区域,在这个区域中可以容纳其他组件,在程序中通常会使面板来实现一些特殊的布局。

普通面板

public class JPanelUse {
    public static void main(String[] args) {
        // 初始化窗口
        JFrame jFrame = new JFrame("面板窗口");
        jFrame.setVisible(true);
        jFrame.setSize(400, 400);
        jFrame.setLocationRelativeTo(null);
        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        
        // 初始化面板:采用默认的流式布局或指定布局
        JPanel jPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
        // 设置面板大小
        jPanel.setSize(100, 100);
        // 设置面板背景颜色
        jPanel.setBackground(new Color(164, 24, 24));
        // 将面板添加到窗口
        jFrame.add(jPanel);
    }
}

滚动面板

JScrollPane,滚动面板,支持水平和垂直滚动视图。文本区域、表格等需要显示较多数据而空间又有限时,通常使用 JScrollPane 进行包裹以实现滚动显示。

public class JScrollPaneUse {
    public JScrollPaneUse() {
        JFrame jFrame = new JFrame("面板窗口");

        // 创建文本区域组件
        JTextArea textArea = new JTextArea("这是一个文本");
        // 自动换行
        textArea.setLineWrap(true);
        // 设置字体
        textArea.setFont(new Font(null, Font.PLAIN, 18));

        // 初始化滚动面板面板
        /**
         * 全参构造参数说明:
         *     view: 需要滚动显示的视图组件
         *     vsbPolicy: 垂直滚动条的显示策略
         *         ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED    // 需要时显示(默认)
         *         ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER        // 从不显示
         *         ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS       // 总是显示
         *     hsbPolicy: 水平滚动条的显示策略
         *         ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED  // 需要时显示(默认)
         *         ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER      // 从不显示
         *         ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS     // 总是显示
         * 常用方法
         *     设置滚动显示视图内容组件:setViewportView(Component view)
         *     设置垂直滚动条的显示策略:setVerticalScrollBarPolicy(int policy)
         *     设置水平滚动条的显示策略:setHorizontalScrollBarPolicy(int policy)
         */
        JScrollPane jScrollPane = new JScrollPane(
                textArea,
                ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
        );
        jFrame.setContentPane(jScrollPane);

        // 窗口设置为公共代码,后面全部省略
        jFrame.setVisible(true);
        jFrame.setSize(400, 400);
        jFrame.setLocationRelativeTo(null);
        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }
}

分隔面板

JSplitPane,分隔面板,用于分隔两个(只能两个)组件,两个组件通过水平/垂直分隔条分别 左右 或 上下 显示,并且可以拖动分隔条调整两个组件显示区域的大小。

class JSplitPaneUse{
    public JSplitPaneUse() {
        JFrame jFrame = new JFrame("分隔面板窗口");

        /**
         * 全参构造参数说明
         *      orientation: 分隔的方向(默认水平),HORIZONTAL_SPLIT:水平左右分隔;VERTICAL_SPLIT:垂直上下分隔
         *      continuousLayout: 拖动分隔条时,是否连续重绘组件,如果为flase,则拖动分隔条停止后才重绘组件。
         *      leftComponent: 左边/上面 显示的组件
         *      rightComponent: 右边/下面 显示的组件
         * 常用方法
         *    setOrientation(int orientation): 设置分隔的方向,水平(左右) 或 垂直(上下) 分隔
         *    setLeftComponent(Component comp):设置 左边/上面 显示的组件
         *    setRightComponent(Component comp):设置 左边/下面 显示的组件
         *    setContinuousLayout(boolean continuousLayout): 设置 拖动分隔条 时是否 连续重绘 组件
         *    setOneTouchExpandable(boolean newValue):分隔条上是否显示快速 折叠/展开 两边组件的小按钮
         *    setDividerSize(int newSize):设置分隔条的大小(宽度)
         *    setDividerLocation(int location):设置分隔条的位置,相对于 左边/顶部 的像素长度
         */
        JSplitPane jSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JButton("左边按钮"), new JButton("右边按钮"));
        jSplitPane.setDividerLocation(200);

        jFrame.setContentPane(jSplitPane);
        // 省略公共代码
    }
}

选项卡面板

JTabbedPane,选项卡面板。它允许用户通过点击给定标题或图标的选项卡,在一组组件之间进行切换显示。

class JTabbedPaneUse {
    public JTabbedPaneUse() {
        JFrame jFrame = new JFrame("选项卡面板窗口");

        /**
         * 全参构造参数说明:
         *     tabPlacement: 选项卡标题的位置, 值为 JTabbedPane.TOP/BOTTOM/LEFT/RIGHT, 默认为 TOP
         *     tabLayoutPolicy: 选项卡位置不能放入所有的选项卡时,放置选项卡的策略,值JTabbedPane.WRAP_TAB_LAYOUT/SCROLL_TAB_LAYOUT
         * 常用方法
         *     addTab(String 标题, Icon 图标, Component 内容组件, String 提示文本):添加选择项卡
         *     insertTab(String title, Icon icon, Component component, String tip, int index):在指定位置插入选项卡
         *     remove(Component component):移除指定内容控件的选项卡
         *     remove(int index):移除指定位置的选项
         *     setSelectedIndex(int index):设置当前选中的选项卡
         *     getSelectedIndex():获取当前选中的选项卡索引
         *     getSelectedComponent():获取当前选中的选项卡对应的内容组件
         *     setTitleAt(int index, String title):设置 index 位置的选项卡的标题
         *     setIconAt(int index, Icon icon):设置 index 位置的选项卡的图标
         *     setEnabledAt(int index, boolean enabled):设置 index 位置的选项卡是否可用
         *     setComponentAt(int index, Component component):将 index 位置的内容组件设置为 component
         */
        // 初始化一个选项面板,默认选项卡在顶部,放不下了换行
        JTabbedPane jTabbedPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT);

        // 创建选项卡
        jTabbedPane.addTab("选项卡1", new JButton("测试按钮"));
        jTabbedPane.addTab("选项卡2", new JButton("测试按钮"));
        jFrame.setContentPane(jTabbedPane);

        // 省略公共代码
    }
}

3、布局

3.1、流式布局

FlowLayout,流式布局管理器,按水平方向依次排列放置组件,排满一行,换下一行继续排列。排列方向(左到右 或 右到左)取决于容器的componentOrientation属性(该属性属于Component),它可能的值如下:

  • ComponentOrientation.LEFT_TO_RIGHT(默认)
  • ComponentOrientation.RIGHT_TO_LEFT

同一行(水平方向)的组件的对齐方式由 FlowLayout 的align属性确定,它可能的值如下:

  • FlowLayout.LEFT : 左对齐
  • FlowLayout.CENTER : 居中对齐(默认)
  • FlowLayout.RIGHT : 右对齐
  • FlowLayout.LEADING : 与容器方向的开始边对齐,例如,对于从左到右的方向,则与左边对齐
  • FlowLayout.TRAILING : 与容器方向的结束边对齐,例如,对于从左到右的方向,则与右边对齐。
class FlowLayoutUse {
    public FlowLayoutUse() {
        JFrame jFrame = new JFrame("流式布局窗口");

        // 创建面板并指定为流式布局
        JPanel jPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
        // 创建两个按钮
        JButton jButton1 = new JButton("按钮1");
        JButton jButton2 = new JButton("按钮2");
        // 将按钮添加到面板中
        jPanel.add(jButton1);
        jPanel.add(jButton2);
        // 将面板添加到窗口中
        jFrame.setContentPane(jPanel);

        // 省略公共代码
    }
}

3.2、网格布局

GridLayout,网格布局管理器,它以矩形网格形式对容器的组件进行布置,把容器按行列分成大小相等的矩形网格,一个网格中放置一个组件,组件宽高自动撑满网格。

以行数和总数优先: 通过构造方法或 setRowssetColumns 方法将行数和列数都设置为非零值时,指定的列数将被忽略。列数通过指定的行数和布局中的组件总数来确定。因此,例如,如果指定了三行和两列,在布局中添加了九个组件,则它们将显示为三行三列。仅当将行数设置为零时,指定列数才对布局有效。

class GridLayoutUse {
    public GridLayoutUse() {
        JFrame jFrame = new JFrame("网格布局窗口");

        // 创建一个面板并使用网格布局
        JPanel jPanel = new JPanel(new GridLayout(2, 2));
        // 创建五个按钮,测试2行2列超出效果
        JButton jButton1 = new JButton("按钮1");
        JButton jButton2 = new JButton("按钮2");
        JButton jButton3 = new JButton("按钮3");
        JButton jButton4 = new JButton("按钮4");
        JButton jButton5 = new JButton("按钮5");
        jPanel.add(jButton1);
        jPanel.add(jButton2);
        jPanel.add(jButton3);
        jPanel.add(jButton4);
        jPanel.add(jButton5);
        jFrame.setContentPane(jPanel);

        // 省略公共代码
    }
}

3.3、边框布局

BorderLayout,边界布局管理器,它把 Container 按方位分为 5 个区域(东、西、南、北、中),每个区域放置一个组件。

class BorderLayoutUse {
    public BorderLayoutUse() {
        JFrame jFrame = new JFrame("网格布局窗口");

        // 创建一个面板并使用边框布局
        JPanel jPanel = new JPanel(new BorderLayout());
        // 创建五个按钮,测试2行2列超出效果
        JButton jButton1 = new JButton("东");
        JButton jButton2 = new JButton("西");
        JButton jButton3 = new JButton("南");
        JButton jButton4 = new JButton("北");
        JButton jButton5 = new JButton("中");
        jPanel.add(jButton1, BorderLayout.EAST);
        jPanel.add(jButton2, BorderLayout.WEST);
        jPanel.add(jButton3, BorderLayout.SOUTH);
        jPanel.add(jButton4, BorderLayout.NORTH);
        jPanel.add(jButton5, BorderLayout.CENTER);
        jFrame.setContentPane(jPanel);

        // 省略公共代码
    }
}

4、组件

4.1、基本组件

标签

JLabel,标签,主要用于展示 文本图片 ,也可以同时显示文本和图片。

class JLabelUse {
    public JLabelUse() {
        JFrame jFrame = new JFrame("标签窗口");
        JPanel jPanel = new JPanel();

        // 只显示文本的标签
        JLabel textLabel = new JLabel("只显示文本的标签");
        textLabel.setFont(new Font(null, Font.PLAIN, 25));
        jPanel.add(textLabel);

        // 只显示图片的标签
        JLabel imgLabel = new JLabel(new ImageIcon("bj.jpg"));
        jPanel.add(imgLabel);

        // 同时显示文本和图片的标签:水平方向文本在图片中心
        JLabel jLabel = new JLabel("显示文本", new ImageIcon("bj.jpg"), SwingConstants.CENTER);
        jPanel.add(jLabel);
		
        jFrame.setContentPane(jPanel);
        // 省略公共代码
    }
}

按钮

class JButtonAndRadioAndCheckBox {
    public JButtonAndRadioAndCheckBox() {
        JFrame jFrame = new JFrame("标签窗口");
        JPanel jPanel = new JPanel();

        /**
         * 普通图片按钮
         */
        JButton jButton = new JButton("图片按钮", new ImageIcon("bj.jpg"));
        jButton.addActionListener(e -> {
            System.out.println("图片按钮被点击了");
        });
        jPanel.add(jButton);

        /**
         * 单选按钮
         */
        // 创建按钮组,将单选按钮添加到该组,确保只能选择其一
        ButtonGroup buttonGroup = new ButtonGroup();
        // 创建单选按钮
        JRadioButton man = new JRadioButton("男");
        JRadioButton woman = new JRadioButton("女");
        // 设置第一个被选中
        man.setSelected(true);
        // 将按钮添加到按钮组中
        buttonGroup.add(man);
        buttonGroup.add(woman);
        // 将按钮添加到面板中
        jPanel.add(man);
        jPanel.add(woman);

        /**
         * 多选按钮
         */
        // 添加多选按钮
        JCheckBox jCheckBox1 = new JCheckBox("香蕉");
        JCheckBox jCheckBox2 = new JCheckBox("苹果");
        JCheckBox jCheckBox3 = new JCheckBox("梨子");
        JCheckBox jCheckBox4 = new JCheckBox("黄瓜");
        // 添加事件监听,添加第一个复选框的状态被改变的监听(其他复选框如果需要监听状态改变,则可按此方法添加监听)
        jCheckBox1.addChangeListener(e -> {
            // 获取事件源(即复选框本身)
            JCheckBox jCheckBox = (JCheckBox) e.getSource();
            System.out.println(jCheckBox.getText() + " 是否选中: " + jCheckBox.isSelected());
        });
        jCheckBox1.setSelected(true);
        jPanel.add(jCheckBox1);
        jPanel.add(jCheckBox2);
        jPanel.add(jCheckBox3);
        jPanel.add(jCheckBox4);


        jFrame.setContentPane(jPanel);
       // 省略公共代码
    }
}

列表

JComboBox,下拉框。JComboBox以下列列表的形式展示多个选项,用户可以从下拉列表中选择一个值。如果设置JComboBox为可编辑状态,除了选择指定的选项值外,还允许用户自行输入值(自行输入的值索引为-1)。

JList,列表框。JList 以列表的形式展示多个选项,允许用户选择一个或多个选项。其中的选项内容由一个 ListModel 实例来维护。JList 不实现直接滚动,需要滚动显示,可以结合 JScrollPane 实现滚动效果。

class JComboBoxAndJList {
    public JComboBoxAndJList() {
        JFrame jFrame = new JFrame("列表窗口");
        JPanel jPanel = new JPanel();
        JLabel jLabel = new JLabel("水果");

        /**
         * 下拉框:这里的数组列表可以使用Vector<String>集合进行
         */
        final JComboBox<String> jComboBox = new JComboBox<>(new String[]{"香蕉", "雪梨", "苹果", "荔枝"});
        // 添加条目选中状态改变的监听器
        jComboBox.addItemListener(e -> {
            // 只处理选中的状态
            if (e.getStateChange() == ItemEvent.SELECTED) {
                System.out.println("选中: " + jComboBox.getSelectedIndex() + " = " + jComboBox.getSelectedItem());
            }
        });
        jPanel.add(jLabel);
        jPanel.add(jComboBox);

        /**
         * 列表框
         */
        final JList<String> jList = new JList<>();
        // 设置一下首选大小
        jList.setPreferredSize(new Dimension(200, 100));
        // 允许可间断的多选
        jList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        // 设置选项数据(内部将自动封装成 ListModel ),这里的数组列表可以使用Vector<String>集合进行
        jList.setListData(new String[]{"香蕉", "雪梨", "苹果", "荔枝"});
        // 添加选项选中状态被改变的监听器
        jList.addListSelectionListener(e -> {
            // 获取所有被选中的选项索引
            int[] indices = jList.getSelectedIndices();
            // 获取选项数据的 ListModel
            ListModel<String> listModel = jList.getModel();
            // 输出选中的选项
            for (int index : indices) {
                System.out.println("选中: " + index + " = " + listModel.getElementAt(index));
            }
            System.out.println();
        });
        jPanel.add(jList);

        jFrame.setContentPane(jPanel);
      // 省略公共代码
    }
}

文本框

JTextField,文本框。JTextField 用来编辑单行的文本。

JPasswordField,密码框。JPasswordField 继承自 JTextField,只是显示输入的内容时用特定的字符替换显示(例如 * 或 ●),用法和 JTextField 基本一致。

JTextArea,文本区域。JTextArea 用来编辑多行的文本。JTextArea 除了允许多行编辑外,其他基本用法和 JTextField 基本一致。

class JTextFieldAndJPasswordFieldAndJTextAreaUse {
    public JTextFieldAndJPasswordFieldAndJTextAreaUse() {
        JFrame jFrame = new JFrame("列表窗口");
        JPanel jPanel = new JPanel(new GridLayout(3,1));

        /**
         * 文本框
         */
        final JTextField jTextField = new JTextField(10);
        jTextField.setFont(new Font(null, Font.PLAIN, 20));
        jPanel.add(new JLabel("用户名"));
        jPanel.add(jTextField);

        /**
         * 密码框
         */
        final JPasswordField jPasswordField = new JPasswordField(32);
        jPanel.add(new JLabel("密 码"));
        jPanel.add(jPasswordField);

        /**
         * 文本域
         */
        // 创建一个 5 行 10 列的文本区域
        JTextArea jTextArea = new JTextArea(5, 10);
        // 自动换行
        jTextArea.setLineWrap(true);
        jPanel.add(new JLabel("文本域"));
        jPanel.add(jTextArea);

        jFrame.setContentPane(jPanel);
        // 省略公共代码
    }
}

进度条和滑块

JProgressBar,进度条。以可视化形式显示某些任务进度的组件,进度条中心可显示进度百分比的文本表示形式。一个任务的进度长度未知时,可将进度条设置为不确定模式。不确定模式的进度条持续地显示动画来表示正进行的操作。当可以确定任务长度和进度量时,则可设置进度条的最大最小值,以及更新进度条的进度值,将其切换回确定模式。

JSlider,滑块。以图形方式在有界区间内通过移动滑块来选择值的组件。滑块可以显示主刻度标记以及主刻度之间的次刻度标记。刻度标记之间的值的个数由 setMajorTickSpacing(int) 和 setMinorTickSpacing(int) 来控制。刻度标记的绘制由 setPaintTicks(boolean) 控制。滑块也可以在固定时间间隔(或在任意位置)沿滑块刻度打印文本标签。标签的绘制由 setLabelTable(Dictionary) 和 setPaintLabels(boolean) 控制。

PS: 当滑块获得焦点后,按键盘上的 上下左右键 也可以滑动滑块。

class JProgressBarAndJSliderUse {
    public JProgressBarAndJSliderUse() {
        JFrame jFrame = new JFrame("列表窗口");
        JPanel jPanel = new JPanel(new GridLayout(3, 1));

        /**
         * 进度条:
         *     全参构造参数说明:JProgressBar(int orient, int min, int max)
         *         orient: 进度条的方向,SwingConstants.VERTICAL 或 SwingConstants.HORIZONTAL,默认为水平方向
         *         min: 最小进度值;max: 最大进度值
         *     常用方法:
         *         设置最小进度值、最大进度值和当前进度值:setMinimum(int min),setMaximum(int max),setValue(int n)
         *         获取当前进度值:getValue()
         *         获取当前进度的百分比:getPercentComplete()
         *         是否绘制百分比文本(进度条中间显示的百分数):setStringPainted(boolean b)
         *         设置进度条进度是否为不确定模式:setIndeterminate(boolean newValue)
         *         设置进度条的方向,SwingConstants.VERTICAL 或 SwingConstants.HORIZONTAL:setOrientation(int newOrientation)
         *         添加进度条的进度改变监听器:addChangeListener(ChangeListener l)
         */
        JProgressBar jProgressBar = new JProgressBar(0, 100);
        jProgressBar.setValue(20);
        jProgressBar.addChangeListener(e -> {
            System.out.println("当前进度值: " + jProgressBar.getValue() + "; " +
                    "进度百分比: " + jProgressBar.getPercentComplete());
        });
        jPanel.add(jProgressBar);

        /**
         * 滑块:
         *     全参构造参数说明:JSlider(int orientation, int min, int max, int value)
         *         orientation: 滑块的方向,SwingConstants.VERTICAL 或 SwingConstants.HORIZONTAL,默认为水平方向
         *         min: 滑块的最小值; max: 滑块的最大值
         *         value: 滑块的初始值(默认为 最小值 和 最大值 之间的 中间值)
         *     常用方法:
         *         设置滑块的最小值、最大值、当前值:setMinimum(int min),setMaximum(int max),setValue(int n)
         *         获取滑块的当前值:getValue()
         *         设置主刻度标记间隔:setMajorTickSpacing(int n)
         *         设置单个主刻度内的次刻度标记间隔:setMinorTickSpacing(int n)
         *         设置是否绘制刻度线:setPaintTicks(boolean b)
         *         设置是否绘制刻度标签(刻度值文本):setPaintLabels(boolean b)
         *         设置是否绘制滑道:setPaintTrack(boolean b)
         *         设置是否颠倒刻度值(刻度值从大到小):setInverted(boolean b)
         *         设置滑块是否对齐到刻度。设置为 true,则滑块最终只能在有刻度的位置取值,即滑块取值不连续:setSnapToTicks(boolean b)
         *         添加滑块的值改变监听器:addChangeListener(ChangeListener l)
         */
        JSlider jSlider = new JSlider(0, 20, 10);
        // 设置主刻度间隔
        jSlider.setMajorTickSpacing(5);
        // 设置次刻度间隔
        jSlider.setMinorTickSpacing(1);
        // 绘制刻度和标签
        jSlider.setPaintTicks(true);
        jSlider.setPaintLabels(true);
        jSlider.addChangeListener(e -> {
            System.out.println("当前值: " + jSlider.getValue());
        });
        jPanel.add(jSlider);

        jFrame.setContentPane(jPanel);
        // 省略公共代码
    }
}

4.2、复杂组件

文件和颜色选择器

JFileChooser,文件选取器。JFileChooser为用户选择文件提供了一种简单的机制,包括打开文件和保存文件。

构造方法和常用方法如下:

方法 功能
JFileChooser(String currentDirectoryPath) currentDirectoryPath: 打开文件选取器时默认显示的文件夹(默认为用户文件夹)
JFileChooser(File currentDirectory) currentDirectory: 打开文件选取器时默认显示的文件夹(默认为用户文件夹)
void setCurrentDirectory(File dir) 设置默认显示的文件夹
void setFileSelectionMode(int mode) 设置文件选择模式,FILES_AND_DIRECTORIES: 文件和文件夹都可以选,其他的二选一
void setMultiSelectionEnabled(boolean b) 设置是否允许同时选择多个(默认为不允许)
void addChoosableFileFilter(FileFilter filter) 添加可供用户选择的文件过滤器
void setFileFilter(FileFilter filter) 设置默认使用的文件过滤器
void setSelectedFile(File file) 设置默认被选中的文件
File[] getSelectedFiles() 获取选择的文件(一般在用户选择完文件点击了确认或保存后通过该方法获取选中的文件)
class FileSelectedUse {
    public FileSelectedUse() {
        JFrame jFrame = new JFrame();
        JPanel jPanel = new JPanel();

        /**
         * 显示 打开文件 或 保存文件 的对话框(线程将被阻塞, 直到选择框被关闭):showOpenDialog(Component parent), showSaveDialog(Component parent)
         *     参数:
         *         parent: 文件选取器对话框的父组件, 对话框将会尽量显示在靠近 parent 的中心; 如果传 null, 则显示在屏幕中心。
         *     返回值:
         *         JFileChooser.CANCEL_OPTION: 点击了取消或关闭
         *         JFileChooser.APPROVE_OPTION: 点击了确认或保存
         *         JFileChooser.ERROR_OPTION: 出现错误
         */
        final JTextArea jTextArea = new JTextArea(10, 30);
        jTextArea.setLineWrap(true);
        jPanel.add(jTextArea);

        JButton openBtn = new JButton("打开");
        openBtn.addActionListener(e -> showFileOpenDialog(jFrame, jTextArea));
        jPanel.add(openBtn);

        JButton saveBtn = new JButton("保存");
        saveBtn.addActionListener(e -> showFileSaveDialog(jFrame, jTextArea));
        jPanel.add(saveBtn);

        jFrame.setContentPane(jPanel);
        // 省略公共代码
    }

    /**
     * 打开文件
     * @param parent 组件
     * @param msgTextArea 文本域
     */
    private static void showFileOpenDialog(Component parent, JTextArea msgTextArea) {
        // 创建一个默认的文件选取器
        JFileChooser fileChooser = new JFileChooser();
        // 设置默认显示的文件夹为当前文件夹
        fileChooser.setCurrentDirectory(new File("."));
        // 设置文件选择的模式(只选文件、只选文件夹、文件和文件均可选)
        fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        // 设置是否允许多选
        fileChooser.setMultiSelectionEnabled(true);
        // 添加可用的文件过滤器(FileNameExtensionFilter 的第一个参数是描述, 后面是需要过滤的文件扩展名 可变参数)
        fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("zip(*.zip, *.rar)", "zip", "rar"));
        // 设置默认使用的文件过滤器
        fileChooser.setFileFilter(new FileNameExtensionFilter("image(*.jpg, *.png, *.gif)", "jpg", "png", "gif"));
        // 打开文件选择框(线程将被阻塞, 直到选择框被关闭)
        int result = fileChooser.showOpenDialog(parent);
        if (result == JFileChooser.APPROVE_OPTION) {
            // 如果点击了"确定", 则获取选择的文件路径
            File file = fileChooser.getSelectedFile();
            // 如果允许选择多个文件, 则通过下面方法获取选择的所有文件
            msgTextArea.append("打开文件: " + file.getAbsolutePath() + "\n\n");
        }
    }

    /**
     * 选择文件保存路径
     * @param parent 组件
     * @param msgTextArea 文本域
     */
    private static void showFileSaveDialog(Component parent, JTextArea msgTextArea) {
        // 创建一个默认的文件选取器
        JFileChooser fileChooser = new JFileChooser();
        // 设置打开文件选择框后默认输入的文件名
        fileChooser.setSelectedFile(new File("测试文件.zip"));
        // 打开文件选择框(线程将被阻塞, 直到选择框被关闭)
        int result = fileChooser.showSaveDialog(parent);
        if (result == JFileChooser.APPROVE_OPTION) {
            // 如果点击了"保存", 则获取选择的保存路径
            File file = fileChooser.getSelectedFile();
            msgTextArea.append("保存到文件: " + file.getAbsolutePath() + "\n\n");
        }
    }
}

JColorChooser,颜色选取器。JColorChooser提供一个用于允许用户操作和选择颜色的控制器对话框。

class ColorSelectedUse {
    public ColorSelectedUse() {
        JFrame jFrame = new JFrame();
        JPanel jPanel = new JPanel();
        final JLabel jLabel = new JLabel();
        jLabel.setPreferredSize(new Dimension(150, 150));
        jLabel.setOpaque(true);
        jPanel.add(jLabel);

        JButton jButton = new JButton("选择颜色");
        jButton.addActionListener(e -> {
            /**
             * 显示一个颜色选取器对话框(线程将被阻塞, 直到对话框被关闭)
             *     参数说明:
             *          component: 对话框的父组件, 对话框将紧靠 component 的中心显示; 如果传 null, 则对话框显示在屏幕中心。
             *          title: 对话框标题。
             *          initialColor: 初始选中的颜色; 如果传 null, 则默认为非透明白色。
             *      返回值:
             *          返回选择的颜色; 如果点击了取消或关闭, 则返回 null。
             */
            Color color = JColorChooser.showDialog(jFrame, "选取颜色", null);
            // 如果用户取消或关闭窗口, 则返回的 color 为 null
            if (color == null) {
                return;
            }
            // 把选取的颜色设置为标签的背景
            jLabel.setBackground(color);
            // 获取颜色的 ARGB 各个分量值
            int alpha = color.getAlpha();
            int red = color.getRed();
            int green = color.getGreen();
            int blue = color.getBlue();
            jLabel.setText("A=" + String.format("%02x", alpha) + ", " +
                    String.format("#%02x%02x%02x", red, green, blue));
        });
        jPanel.add(jButton);

        jFrame.setContentPane(jPanel);
        // 省略公共代码
    }
}

菜单栏和工具栏

JMenuBar,菜单栏。菜单栏组件添加到 JFrame 窗口后,在窗口的内容显示区域的顶部出现。实现一个菜单栏主要涉及三种类:

  • JMenuBar:表示一个菜单栏。
  • JMenu:表示菜单栏上的一个一级菜单。
  • JMenuItem, JCheckBoxMenuItem, JRadioButtonMenuItem:表示一级菜单下的一个子菜单项,三者分别表示 普通的子菜单、带复选框的子菜单、带单选按钮的子菜单。

PS: JMenu 继承自 JMenuItem,所以一个 JMenu 也可以当做是一个二级子菜单项,通过 JMenu 和 JMenuItem 之间的嵌套,可实现多级子菜单效果。

构造参数和常用方法如下

方法 功能
JMenuItem void setText(String text) 设置菜单显示的文本
JMenuItem void setIcon(Icon defaultIcon) 设置菜单显示的图标
全参构造 JMenuItem(String text, Icon icon) text: 菜单显示的文本,icon: 菜单显示的图标
JMenuItem void setMnemonic(int mnemonic) 设置菜单的键盘助记符
JMenuItem void setAccelerator(KeyStroke keyStroke) 设置修改键,使用键盘快捷键直接触发菜单项的动作
JMenuItem void addActionListener(ActionListener l) 添加菜单被点击的监听器
JMenuItem void setActionCommand(String actionCommand) 可以再监听器回调时通过命令名称区别是哪个菜单项触发的动作。
JMenu JMenuItem add(JMenuItem menuItem) 添加子菜单到JMenu中
JMenu void addSeparator() 添加一个子菜单分割线
class JMenuBarUse{
    public JMenuBarUse() {
        JFrame jFrame = new JFrame();
        JPanel jPanel = new JPanel();

        // 创建一个菜单栏
        JMenuBar jMenuBar = new JMenuBar();
        
        // 创建一级菜单
        JMenu fileMenu = new JMenu("文件");
        JMenu editMenu = new JMenu("编辑");
        // 将一级菜单添加到菜单栏
        jMenuBar.add(fileMenu);
        jMenuBar.add(editMenu);
        
        // 创建文件菜单的子菜单
        JMenuItem openMenuItem  = new JMenuItem("打开");
        JMenuItem newMenuItem  = new JMenuItem("新建");
        fileMenu.add(newMenuItem);
        fileMenu.add(openMenuItem);

        jPanel.add(jMenuBar);
        // 省略公共代码
    }
}

JToolBar,工具栏。JToolBar 提供了一个用来显示常用控件的容器组件。

对于大多数的外观,用户可以将工具栏拖到其父容器四“边”中的一边,并支持在单独的窗口中浮动显示。为了正确执行拖动,建议将 JToolBar 实例添加到容器四“边”中的一边(其中容器的布局管理器为 BorderLayout),并且不在其他四“边”中添加任何子级。

构造方法和常用方法如下:

方法 功能
JToolBar(String name, int orientation) name: 工具栏名称,悬浮显示时为悬浮窗口的标题。orientation: 工具栏方向,默认水平
Component add(Component comp) 添加 工具组件 到 工具栏
void addSeparator(Dimension size) 添加 分隔符组件 到 工具栏
Component getComponentAtIndex(int index) 获取工具栏中指定位置的组件(包括分隔符)
void setFloatable(boolean b) 设置工具栏是否可拖动
void setOrientation(int o) 设置工具栏方向,值为 SwingConstants.HORIZONTAL 或 SwingConstants.VERTICAL
void setMargin(Insets m) 设置工具栏边缘和其内部工具组件之间的边距(内边距)
void setBorderPainted(boolean b) 是否需要绘制边框
class JToolBarUse{
    public JToolBarUse() {
        JFrame jFrame = new JFrame();
        JPanel jPanel = new JPanel();

        // 创建一个工具栏
        JToolBar jToolBar = new JToolBar("测试工具栏");
        JButton jButton = new JButton("按钮");
        jToolBar.add(jButton);

        jPanel.add(jToolBar);
        // 省略公共代码
    }
}

5、事件

5.1、鼠标监听事件

class MouseListenerUse {
    public MouseListenerUse() {
        JFrame jFrame = new JFrame("鼠标监听");
        JPanel jPanel = new JPanel();

        /**
         * 鼠标监听器
         */
        jPanel.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseEntered(MouseEvent e) {
                System.out.println("鼠标进入组件区域");
            }

            @Override
            public void mouseExited(MouseEvent e) {
                System.out.println("鼠标离开组建区域");
            }

            @Override
            public void mousePressed(MouseEvent e) {
                // 获取按下的坐标(相对于组件)
                System.out.println("相对组件" + e.getPoint() + ",横坐标:" + e.getX() + ", 纵坐标:" + e.getY());
                // 获取按下的坐标(相对于屏幕)
                System.out.println("相对屏幕" + e.getLocationOnScreen() + ",横坐标:" + e.getXOnScreen() + ", 纵坐标:" + e.getYOnScreen());
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                System.out.println("鼠标释放");
            }

            @Override
            public void mouseClicked(MouseEvent e) {
                // 鼠标在组件区域内按下并释放(中间没有移动光标)才识别为被点击
                System.out.println("鼠标点击");
            }
        });

        /**
         * 鼠标移动/拖动监听器
         */
        jPanel.addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                // 鼠标保持按下状态移动即为拖动
                System.out.println("鼠标拖动");
            }
            @Override
            public void mouseMoved(MouseEvent e) {
                System.out.println("鼠标移动");
            }
        });


        /**
         * 鼠标滚轮监听器
         */
        jPanel.addMouseWheelListener(new MouseWheelListener() {
            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                // e.getWheelRotation() 为滚轮滚动多少的度量
                System.out.println("mouseWheelMoved: " + e.getWheelRotation());
            }
        });

       // 省略公共代码
    }
}

5.2、键盘监听事件

组件监听键盘的按键,该组件必须要获取到焦点。

如果一个窗口内没有可获取焦点的组件,一般打开窗口后焦点为窗口所有,可以把键盘监听器设置到窗口(JFrame)身上。

如果窗口内还有其他组件可获取焦点(例如按钮、文本框),窗口打开后焦点会被内部组件获得,如果想要在窗口打开期间都能监听键盘按键,可以为所有可获得焦点的组件都设置一个键盘监听器。

class KeyListenerUse{
    public KeyListenerUse() {
        JFrame jFrame = new JFrame("键盘监听");
        jFrame.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                // 获取键值,和 KeyEvent.VK_XXXX 常量比较确定所按下的按键
                System.out.println("按下: " + e.getKeyCode() + ",键值为:" + e.getKeyCode());
            }

            @Override
            public void keyTyped(KeyEvent e) {
                // e.getKeyChar() 获取键入的字符
                System.out.println("键入: " + e.getKeyChar());
            }

            @Override
            public void keyReleased(KeyEvent e) {
                System.out.println("释放: " + e.getKeyCode());
            }
        });
        jFrame.setVisible(true);
    }
}

5.3、窗口监听事件

窗口监听器只有窗口类组件支持,例如 JFrame、JDialog。目前经过测试,使用最多的莫过于窗口关闭和窗口激活。

class WindowListenerUse{
    public WindowListenerUse() {
        JFrame jFrame = new JFrame("窗口监听");
        jFrame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.out.println("窗口被关闭了");
            }

            @Override
            public void windowActivated(WindowEvent e) {
                System.out.println("窗口被激活了");
            }
        });
        jFrame.setVisible(true);
    }
}

Java GUI setSize()setPreferredSize()的区别

setSize()setPreferredSize()都可以设置组件的大小,但二者的使用有所不同。

1、setSize()的使用方式

  • setSize(int width,int height)
  • setSize(Dimension d)

2、etPreferredSize()的使用方式

  • setSize(Dimension d)

setPreferredSize()是设置首选尺寸。一般情况下:

  • 如果该组件受布局管理器管理(使用默认的也算),需要使用setPreferredSize()设置尺寸,setSize()无效。
  • 如果该组件不受布局管理器管理,需要使用setSize(),setPreferredSize()无效。

比如:

JFrame设置了布局管理器,往JFrame中添加一个JLabelJLabel受布局管理器管理,需要使用setPreferredSize()setSize()无效。受指的是JLabel,不是布局管理器所在的容器JFrameJFrame没有受到布局管理器的管理,要使用setSize()

是说该组件受到布局管理器的管理,不是说该组件本身设置了布局管理器。

3、Dimension的使用方式

构造函数:

  • Dimension(int width,int height) //只能是int ,不能是double

常用方法:

  • getWidth/Height() //返回值均为double。也可直接访问属性width、height。
  • setSize(int width,int height) //不能单独设置其中一个
  • setSIze(double width,double height)

Optional

posted @ 2024-05-25 22:47  Yang0710  阅读(16)  评论(0编辑  收藏  举报