一、代码块
1.静态代码块
格式:static{ }
特点:类加载时自动执行,由于类只加载一次,所以静态代码块也只执行一次
作用:完成类的初始化,常用于对静态内容的初始化赋值(例如对静态变量的初始化赋值)
public class CodeDemo1 {
//创建静态数组(扑克牌)
static final String[] cards = new String[54];
//创建静态代码块
static{
//加载类的同时创建扑克牌数组
cards[0] = "大王";
cards[1] = "小王";
System.out.println("静态代码块执行了");
}
public static void main(String[] args) {
System.out.println("main方法执行了");
System.out.println(cards[0]);
}
}
//执行输出结果:
//静态代码块执行了
//main方法执行了
//大王
2.实例代码块
格式:{ }
特点:每次创建对象时,执行实例代码块,并在构造器前执行
作用:和构造器一样用于完成对象的初始化(例如每次创建对象时,自动对实例变量进行初始化赋值)
public class CodeDemo2 {
//创建数组,存储姓名
private String[] names = new String[3];
//创建实例代码块
{
//初始化对象的属性
names[0] = "张三";
System.out.println("实例代码块执行了");
}
public static void main(String[] args) {
System.out.println("main方法执行了");
CodeDemo2 codeDemo2 =new CodeDemo2();
System.out.println(codeDemo2.names[0]);
}
}
//打印结果:
//main方法执行了
//实例代码块执行了
//张三
二、内部类
1.内部类介绍
1)一个类定义在另一个类的内部,这个类就是内部类,当一个类的内部包含了完整的事物,且该事物没有单独设计的必要时,就可以将该事物设计为内部类,示例:
//汽车
public class Car {
//发动机
public class Engine {
}
}
2)内部类分为4种:成员内部类、静态内部类、局部内部类、匿名内部类
2.成员内部类
1)成员内部类的语法
//外部类
public class Outer {
//创建成员内部类,属于外部类的对象持有
public class Inner {
public void show() {
System.out.println("内部类的show()方法执行了");
}
}
}
2)创建对象的语法
public class InnerClassDemo1 {
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner();
inner.show();
//打印结果:内部类的show方法执行了
}
}
3)成员内部类访问外部类成员的特点:内部类可直接访问外部类的静态成员和实例成员
//外部类
public class Outer {
//创建外部类的静态成员
public static String name = "张三";
//创建外部类的静态方法
public static void show() {}
//创建外部类的实例成员
public String age = "18";
//创建外部类的实例方法
public void test() {}
//创建成员内部类
public class Inner {
public void show() {
//访问外部类的静态成员
System.out.println("外部类的静态成员:" + name);
//访问外部类的实例成员
System.out.println("外部类的实例成员:" + age);
//访问外部类的静态方法
show();
//访问外部类的实例方法
test();
}
}
}
4)成员内部类的实例方法中,可以直接拿到当前寄生的外部类对象,使用方式:外部类名.this
public class InnerClassDemo1 {
public static void main(String[] args) {
//创建成员内部类对象
People.Student student = new People().new Student();
student.show(); //打印结果:30 25 18
}
}
//创建外部类
class People {
//创建外部类成员变量
private int age = 18;
//创建成员内部类
public class Student {
//创建成员内部类的成员变量
private int age = 25;
//创建成员内部类的实例方法
public void show()
{
//创建实例方法中的局部变量
int age = 30;
//打印方法中的局部变量
System.out.println(age); //30
//打印成员内部类的成员变量
System.out.println(this.age); //25
//打印外部类的成员变量
System.out.println(People.this.age); //
}
}
}
总结:
①成员内部类就是类中的一个普通成员,创建对象的方式为:外部类名.内部类名 对象名 = new 外部类(…).new 内部类(…);
②成员内部类的实例方法中,可以直接访问外部类的实例成员、静态成员
③在成员内部类的实例方法中拿到外部类对象的格式:外部类名.this
3.静态内部类
1)静态内部类语法,内部类名添加static修饰
//创建外部类
public class Outer {
//创建静态内部类
static class Inner{
public void show(){
System.out.println("静态内部类");
}
}
}
2)创建对象调用静态内部类的方法
public class InnerClassDemo {
public static void main(String[] args) {
//创建对象语法:外部类名.内部类名 对象名 = new 外部类名.内部类名();
Outer.Inner oi = new Outer.Inner();
//调用静态内部类的方法
oi.show();
}
}
注意:
①内部类名使用static修饰,静态内部类属于外部类自己持有,不属于外部类的对象
②创建对象的方式为:外部类名.内部类名 对象名 = new 外部类名.内部类名();
③静态内部类中可以直接访问外部类的静态成员,但无法直接访问外部类的实例成员(实例成员属于外部类的对象)
4.局部内部类
在方法中定义内部类(普通类、抽象类、接口),一般无实际意义,仅做了解
public class Test {
public static void main(String[] args) {
}
public void show() {
class A {
}
abstract class B{
}
interface C{
}
}
}
5.匿名内部类(重点)
1)认识匿名内部类
一种特殊的局部内部类,匿名是指不需要为这个类声明类名,默认存在隐藏的类名;匿名内部类本质上是一个子类,创建的时候同时也会创建出一个子类对象(作用:更方便的创建子类对象)。
例如:有个动物父类,存在食物方法,每个动物需要创建子类继承父类重写食物方法(每个动物爱吃的食物不同),使用匿名内部类则无需单独创建具体的动物子类。
创建动物父类
//创建抽象动物父类
public abstract class Animal {
//定义抽象食物方法
public abstract void eat();
}
使用匿名内部类语法
public class Test {
public static void main(String[] args) {
//使用匿名内部类语法,创建子类:猫,同时会自动构建猫对象
Animal cat = new Animal() {
//重写动物父类的食物方法
@Override
public void eat() {
System.out.println("猫喜欢吃猫粮");
}
};
cat.eat(); //打印结果:猫喜欢吃骨头
//使用匿名内部类语法,创建子类:狗,同时会自动构建狗对象
Animal dog = new Animal() {
//重写动物父类的食物方法
@Override
public void eat() {
System.out.println("狗喜欢吃骨头");
}
};
dog.eat(); //打印结果:狗喜欢吃肉
}
}
查看out目录编译后的class文件,可以发现匿名内部类默认存在的隐藏类名(Test$1和Test 2 ),隐藏类名格式:匿名内部类所在的类名 + 2),隐藏类名格式:匿名内部类所在的类名+ 2),隐藏类名格式:匿名内部类所在的类名++序号
查看编译后的class文件内容,可以看到继承自动物父类,印证开头的“匿名内部类本质上是一个子类”
class Test$1 extends Animal {
Test$1() {
}
public void eat() {
System.out.println("猫喜欢吃猫粮");
}
}
2)匿名内部类语法的常见使用形式
常见使用形式:通常可以作为一个对象参数传给方法使用
需求:学生、老师参加游泳比赛,思路:将游泳比赛定义为方法,学生、老师定义为匿名内部类(对象)传递给游泳比赛方法使用
public class Test2 {
public static void main(String[] args) {
//创建学生匿名内部类(方法一)
swim student = new swim() {
//重写接口的抽象方法
@Override
public void swiming() {
System.out.println("学生游完用了20秒");
}
};
swim(student); //打印结果:开始游泳\n学生游完用了20秒
//创建老师匿名内部类(方法二,直接将匿名内部类创建语法放进游泳比赛方法)
swim(new swim() {
@Override
public void swiming() {
System.out.println("老师游完用了10秒");
}
});
//打印结果:开始游泳\n老师游完用了10秒
}
//创建游泳比赛方法
public static void swim(swim s){
System.out.println("开始游泳");
s.swiming();
}
}
//定义接口和游泳方法用于创建匿名内部类
interface swim{
void swiming();
}
3)使用场景1-传输匿名内部类对象给方法使用
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Test3 {
public static void main(String[] args) {
//创建窗口
JFrame jf = new JFrame("登录窗口");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setBounds(100,100,500,300);
jf.setVisible(true);
jf.setLayout(null);
//创建登录按钮
JButton jb = new JButton("登录");
jb.setBounds(200,200,100,50);
//在窗口中添加登录按钮
jf.add(jb);
//给登录按钮添加监听器,点击时候执行代码
//匿名内部类的使用场景:调用别人的方法实现需求时(监听点击按钮的动作),该方法正好允许传输一个匿名内部类对象给方法使用
jb.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("登录成功!");
}
});
}
}
4)使用场景2-使用comparator接口
使用comparator接口的匿名内部类实现对数组的排序
需求:有一个学生数组,存储了学生的姓名的分数,需要对分数高低进行排序
创建学生信息类:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private double score;
}
创建数组初始化数据并排序:
import java.util.Arrays;
import java.util.Comparator;
public class Test4 {
public static void main(String[] args) {
//创建一个Student数组
Student[] s = new Student[5];
//创建5个Student对象
s[0] = new Student("张三", 73);
s[1] = new Student("李四", 85);
s[2] = new Student("王五", 92);
s[3] = new Student("赵六", 66);
s[4] = new Student("孙七", 97);
//调用sun公司提供的sort方法对数组s进行排序
//参数一:需要排序的数组
//参数二:声明一个匿名内部类,实现Comparator接口,重写compare方法:定义排序规则
Arrays.sort(s, new Comparator<Student>() {
@Override
//传入两个Student对象,比较两个Student对象的score属性,并返回结果
public int compare(Student s1, Student s2) {
//当左边减去右边的值为正数时,返回正数,表示s1比s2大
//当左边减去右边的值为负数时,返回负数,表示s1比s2小
//当左边减去右边的值为0时,返回0,表示s1和s2相等
return (s1.getScore() - s2.getScore()); //升序排序,降序排序改为s2.getScore() - s1.getScore()即可
}
});
System.out.println("排序后的结果:");
for (int i = 0; i < s.length; i++) {
System.out.println(s[i]);
}
}
}
三、Lambda函数
1.理解Lambda函数
JDK8新增的一种语法形式,表示函数。用于替代【函数式接口】的匿名内部类对象,使程序更简洁。
函数式接口:有且仅有一个抽象方法的接口
语法示例:
public class LambdaDemo1 {
public static void main(String[] args) {
//常规方式:创建匿名内部类实现函数式接口(创建接口对象)
/*swim s = new swim() {
@Override
public void swiming() {
System.out.println("开始游泳");
}
};
s.swiming();*/
//使用Lambda表达式
swim s = () -> {
System.out.println("开始游泳");
};
s.swiming();
}
}
//声明函数式接口的注解,当接口中的抽象方法唯一时,可以省略
//作用:更安全,保证接口中只有一个抽象方法,否则会报错
@FunctionalInterface
interface swim {
void swiming();
}
2.Lambda表达式的省略规则
①参数类型可以全部不写
②如果只有一个参数,省略参数类型的同时还可以省略参数的括号(),但多个参数不能省略括号()
③如果lambda表达式中只有一行代码,还可以省略大括号和这行代码的分号,若这行代码是return语句,则必须省略掉return
示例1,通过lambda表达式简化2.5.4中对数组的排序功能:
public class Test4 {
public static void main(String[] args) {
//创建一个Student数组
Student[] s = new Student[5];
//创建5个Student对象
s[0] = new Student("张三", 73);
s[1] = new Student("李四", 85);
s[2] = new Student("王五", 92);
s[3] = new Student("赵六", 66);
s[4] = new Student("孙七", 97);
//调用sun公司提供的sort方法对数组s进行排序
//参数一:需要排序的数组
//参数二:声明一个匿名内部类,实现Comparator接口,重写compare方法:定义排序规则
Arrays.sort(s, new Comparator<Student>() {
@Override
//传入两个Student对象,比较两个Student对象的score属性,并返回结果
public int compare(Student s1, Student s2) {
//当左边减去右边的值为正数时,返回正数,表示s1比s2大
//当左边减去右边的值为负数时,返回负数,表示s1比s2小
//当左边减去右边的值为0时,返回0,表示s1和s2相等
return (s1.getScore() - s2.getScore()); //升序排序,降序排序改为s2.getScore() - s1.getScore()即可
}
});
//使用lambda表达式进行简化:将方法参数替换匿名内部类
Arrays.sort(s, (Student s1, Student s2) -> {
return s1.getScore() - s2.getScore();
});
//继续简化:省略参数类型的写法
Arrays.sort(s, (s1,s2) -> {
return s1.getScore() - s2.getScore();
});
//继续简化:省略return 和 {}
Arrays.sort(s, (s1,s2) -> s1.getScore() - s2.getScore());
System.out.println("排序后的结果:");
for (int i = 0; i < s.length; i++) {
System.out.println(s[i]);
}
}
}
示例2,通过lambda表达式简化2.5.3中监听点击按钮功能
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Test3 {
public static void main(String[] args) {
//创建窗口
JFrame jf = new JFrame("登录窗口");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setBounds(100,100,500,300);
jf.setVisible(true);
jf.setLayout(null);
//创建登录按钮
JButton jb = new JButton("登录");
jb.setBounds(200,200,100,50);
//在窗口中添加登录按钮
jf.add(jb);
//给登录按钮添加监听器,点击时候执行代码
//匿名内部类的使用场景:调用别人的方法实现需求时(监听点击按钮的动作),该方法正好允许传输一个匿名内部类对象给方法使用
jb.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("登录成功!");
}
});
//使用lambda表达式简化:将方法参数替换匿名内部类
jb.addActionListener((ActionEvent e) -> {
System.out.println("登录成功!");
});
//继续简化:省略参数类型且只有一个参数时可以省略参数括号
jb.addActionListener(e -> {
System.out.println("登录成功!");
});
//继续简化:省略{}
jb.addActionListener(e -> System.out.println("登录成功!"));
}
}
3.静态方法引用
当lambda表达式中只是调用一个静态方法且箭头"->"前后的参数一致时,即可使用静态方法引用
以2.5.4的数组排序为例,将排序功能由程序入口改为封装在父类中作为静态方法,通过lambda静态方法引用
创建学生父类并定义对数组排序的静态方法:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int score;
//定义静态方法对数组元素(分数)进行排序
public static int compareScore(Student s1,Student s2){
return s1.getScore() - s2.getScore();
}
}
lambda静态方法引用示例:
import java.util.Arrays;
public class Test4 {
public static void main(String[] args) {
//创建一个Student数组
Student[] s = new Student[5];
//创建5个Student对象
s[0] = new Student("张三", 73);
s[1] = new Student("李四", 85);
s[2] = new Student("王五", 92);
s[3] = new Student("赵六", 66);
s[4] = new Student("孙七", 97);
/* 调用Arrays的sort方法对数组s进行排序
* 当lambda表达式里只有一个静态方法且箭头前后的参数类型一致时,可使用静态方法引用简化
Arrays.sort(s,(s1,s2) -> Student.compareScore(s1,s2));
*/
//优化写法:lambda引用静态方法(类名::静态方法名)
Arrays.sort(s,Student::compareScore);
System.out.println("排序后的结果:");
for (int i = 0; i < s.length; i++) {
System.out.println(s[i]);
}
}
}
4.实例方法引用
当lambda表达式中只是通过对象名调用一个实例方法且箭头"->"前后的参数一致时,即可使用实例方法引用
创建学生父类并定义对数据排序的实例方法:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int score;
private double height;
//定义静态方法对数组元素进行排序
public static int compareScore(Student s1,Student s2){
return s1.getScore() - s2.getScore();
}
//定义实例方法对数组元素(身高)进行排序
public int compareHeight(Student s1,Student s2){
//不能直接调用s1.getHeight() - s2.getHeight(),因为getHeight()返回的是double类型
//也不能使用int强转s1.getHeight() - s2.getHeight()的结果,例如:(int)s1.getHeight() - s2.getHeight()
//当171.2-171时,结果会被强转为0,而不是1,导致排序失败
//正确写法:使用Double.compare()方法
return Double.compare(s1.getHeight(),s2.getHeight());
}
}
lambda实例方法引用示例:
import java.util.Arrays;
public class Test5 {
public static void main(String[] args) {
//创建一个Student数组
Student[] s = new Student[5];
//创建5个Student对象
s[0] = new Student("张三", 73,180);
s[1] = new Student("李四", 85,170);
s[2] = new Student("王五", 92,183.5);
s[3] = new Student("赵六", 66,155);
s[4] = new Student("孙七", 97,168);
//创建对象
Student temp = new Student();
/*调用Arrays的sort方法对数组s进行排序
*当lambda表达式中只是通过对象调用一个实例方法,且箭头"->"前后参数类型一致时,可使用实例方法引用简化
Arrays.sort(s, (s1, s2) -> temp.compareHeight(s1, s2));*/
//lambda实例方法引用写法:对象名::实例方法名
Arrays.sort(s, temp::compareHeight);
System.out.println("排序后的结果:");
for (int i = 0; i < s.length; i++) {
System.out.println(s[i]);
}
}
}
5.特定类型的方法引用
使用场景:如果某个lambda表达式里只是调用一个特定类型的实例方法,并且箭头"->"前面参数列表中的第一个参数是作为后面实例方法的主调,后面的所有参数都是作为该方法的入参时,则可以使用特定类型的方法引用
需求1:创建一个字符串数组,对数组中的名字进行字符串排序:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
String[] name = { "Angela", "jack", "Tom", "allen", "bob" , "lucy" , "Zac", "Mike", "jerry"};
Arrays.sort(name);
System.out.println(Arrays.toString(name));
//排序结果:[Angela, Mike, Tom, Zac, allen, bob, jack, jerry, lucy]
//默认排序规则:按照字符串的ASCII码值进行排序,导致大写字符串排前面,小写字符串排后面
//当jack, jerry都是j开头时,第二位e比a的ASCII码值大,所以jack排前面,jerry排后面
}
}
需求2:忽略字符串的首字母大小写进行排序(大写A和小写a都拍到最前面)
import java.util.Arrays;
import java.util.Comparator;
public class Test {
public static void main(String[] args) {
String[] name = { "Angela", "jack", "Tom", "allen", "bob" , "lucy" , "Zac", "Mike", "jerry"};
Arrays.sort(name, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//调用String类中的compareToIgnoreCase方法可以实现忽略大小写的排序
return o1.compareToIgnoreCase(o2);
}
});
System.out.println(Arrays.toString(name));
//打印结果:[allen, Angela, bob, jack, jerry, lucy, Mike, Tom, Zac]
}
}
需求3:将需求2的实现代码使用lambda特定类型方法引用进行简化
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
String[] name = { "Angela", "jack", "Tom", "allen", "bob" , "lucy" , "Zac", "Mike", "jerry"};
/*默认实现方式
Arrays.sort(name, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//调用String类中的compareToIgnoreCase方法可以实现忽略大小写的排序
return o1.compareToIgnoreCase(o2);
}
});*/
/*改为lambda表达式的简化结果:
Arrays.sort(name, (o1,o2) -> o1.compareToIgnoreCase(o2));
* 同时符合以下情况,可使用特定类型方法引用进一步简化
* 1.表达式中调用一个特定类型的实例方法(compareToIgnoreCase为特定类型String的方法)
* 2.箭头前面第一个参数作为后面方法的主调(o1做为compareToIgnoreCase方法的主调)
* 3.后面参数作为方法入参(o2作为compareToIgnoreCase方法参数)
*/
//改为lambda特定类型的方法引用进一步简化,写法:特定类名称::方法名
Arrays.sort(name, String::compareToIgnoreCase);
System.out.println(Arrays.toString(name));
//打印结果:[allen, Angela, bob, jack, jerry, lucy, Mike, Tom, Zac]
}
}
6.构造器引用
使用场景:如果lambda表达式中只是在创建对象,并且箭头"->"前后的参数一致,就可以使用构造器引用
造例子体现lambda构造器引用:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
public class Test2 {
public static void main(String[] args) {
/*创建接口的匿名内部类对象实现接口
CreateCar cc = new CreateCar() {
@Override
public Car create(String name) {
return new Car();
}
};*/
//使用lambda表达式简化匿名内部类
//CreateCar cc = name -> new Car();
//继续使用lambda构造器引用简化,语法:类名::new
CreateCar cc = Car::new;
//创建汽车
Car c = cc.create("保时捷");
System.out.println(c);
}
}
//创建函数式接口定义创建汽车的抽象方法
@FunctionalInterface
interface CreateCar{
Car create(String name);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
//创建汽车类
class Car{
private String name;
}
四、常用API
1.String
作用:创建字符串对象,封装要处理的字符串,或提供方法处理字符串内容
1)使用String封装字符串的几种语法
public class StringDemo1 {
public static void main(String[] args) {
//常用方式一:字面量定义字符串,直接使用""即可创建字符串对象,封装字符串数据
String s1 = "hello";
//不常用方式:通过构造器创建字符串对象
String s2 = new String(); //创建一个空字符串对象,不推荐的使用方式
String s3 = new String("hello"); //创建一个字符串对象,封装字符串数据,不推荐使用
//根据字符数组创建字符串对象
char[] c = {'h','e','l','l','o'};
String s4 = new String(c);
System.out.println(s4); //打印结果:hello
//根据字节数组创建字符串对象
byte[] b = {104,101,108,108,111}; //hello对应的ASCII码
String s5 = new String(b);
System.out.println(s5); //打印结果:hello
}
}
2)不同方式创建String对象的区别:通过常用方式" "创建的字符串对象,会存储到字符串常量池,且相同内容的字符串只存储一份;通过new方式创建的字符串对象,每new一次都会产生一个新的对象放在堆内存中
public class StringDemo2 {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); //打印结果: true
//原因:直接创建字符串对象,字符串对象放在字符串常量池中,且内容相同的字符串只存一个,所以s1和s2指向同一内存地址
//目的:节约内存空间,提高效率
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println(s3 == s4); //打印结果: false
//原因:new创建字符串对象,字符串对象放在堆内存中,每new一次,就会创建一个对象,所以s3和s4指向不同的内存地址
}
}
3)调用String的方法, 处理字符串数据
注意:
①对比两个字符串的值是否一致不能使用 ==,例如接收输入内容时创建的字符串对象与系统进行内容对比的字符串对象内存地址不同,尽管两个字符串的值相同,对比结果也只能是false;使用equals方法对比则只关注两个字符串的内容,不关心内存地址。
②比对验证码是否一致的场景中需要忽略大小写时,则推荐使用equalsIgnoreCase方法
4)案例:实现验证码生成
需求:随机生成验证码,验证码可能是数字、大写字母或小写字母
public class StringDemo3 {
public static void main(String[] args) {
System.out.println(getRandomCode(6));
}
//随机生成验证码
public static String getRandomCode(int n) {
//定义空字符串用于拼接生成的随机字符
String code = "";
//定义验证码所需的字符集
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
//根据入参的整数循环生成n位随机字符
for (int i = 0; i < n; i++) {
//生成随机索引
int index = (int)(Math.random() * str.length()); // [0,1) * [0,62) = [0,61]
//拼接随机字符
code += str.charAt(index);
}
return code;
}
}
2.ArrayList
1)什么是集合?
集合属于一种容器,用来装数据,类似数组。但数组长度一开始就固定了,集合的大小可动态变更,ArrayList则是一种常用的集合。属于泛型类,可以通过<>中的内容约束存储的数据类型,使用方式:先调用ArrayList的无参构造器创建集合对象,然后使用增删改查的对应方法操作集合。
import java.util.ArrayList;
public class ArrayListdemo {
public static void main(String[] args) {
//创建集合,若<>中没有指定数据类型,则可以存入任意数据类型
ArrayList<String> list = new ArrayList<>();
//添加数据
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
//遍历集合查询数据
for (int i = 0; i < list.size(); i++) { //获取集合的长度
System.out.println(list.get(i)); //根据索引获取集合中的元素
}
//修改数据
list.set(0, "小张"); //set方法:修改集合中索引为0的元素
System.out.println(list.get(0));
//删除数据方式1,通过索引删除
list.remove(0);
//删除数据方式2,通过元素值删除
list.remove("赵六");
}
}
2)ArrayList的常用方法:
五、GUI编程
1.GUI是什么?
1)GUI:Graphical User Interface 图形用户界面,使用较少,并非主流,设计一个界面代码臃肿。
2)通过图形元素(窗口、按钮、文本框等)与用户进行交互
3)与命令行界面(CLI)相比,GUI更直观、友好
4)Java提供了两套GUI编程库:AWT(Abstract Window Toolkit)抽象窗口工具集,提供一组原生GUI组件,但依赖操作系统的本地窗口系统,例如在Windows能运行在macOS就无法运行,逐渐淘汰。Swing:基于AWT,提供了更丰富份GUI组件,不依赖本地窗口系统
5)常用是Swing组件:JFrame(窗口)、JPanel(放在窗口中的面板或画布,用于组织其他组件的容器)、JButton(按钮)、JTextField(输入框)、JTable(表格)……
2.常见的布局管理器
作用:决定组件在容器中的布局方式,避免手动设置每个组件的位置和大小,简化GUI设计过程
1)FlowLayout:最简单的布局管理器,它按水平方向从左到右排列组件,当一行排满时,自动换到下一行
特点:默认居中对齐,可以设置为左对齐或右对齐,适用于需要简单排列的场景
import javax.swing.*;
import java.awt.*;
public class FlowLayoutDemo {
public static void main(String[] args) {
JFrame jf = new JFrame("流式布局"); //创建窗体
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //设置关闭模式:点击X退出时关闭程序
jf.setSize(400,300); //设置窗体大小
jf.setVisible(true); //显示窗体
jf.setLayout(new FlowLayout()); //设置窗体的布局管理器
jf.add(new JButton("登录")); //添加按钮
jf.add(new JButton("注册")); //添加按钮
jf.add(new JButton("忘记密码")); //添加按钮
jf.add(new JButton("帮助")); //添加按钮
jf.add(new JButton("关于")); //添加按钮
jf.add(new JButton("退出")); //添加按钮
}
}
界面效果:
2)BorderLayout:将容器划分为五个区域:东、南、西、北和中(East, South,West,North,center),每个区域只能添加一个组件,未添加组件的区域保持空白
特点:适用于需要在特定区域布局组件的场景,中间区域会占据所有剩余的空间
import javax.swing.*;
import java.awt.*;
public class BorderLayoutDemo {
public static void main(String[] args) {
JFrame jf = new JFrame("边界布局");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(400,300);
jf.setVisible(true);
jf.setLayout(new BorderLayout());
jf.add(new JButton("上"),BorderLayout.NORTH);
jf.add(new JButton("下"),BorderLayout.SOUTH);
jf.add(new JButton("左"),BorderLayout.WEST);
jf.add(new JButton("右"),BorderLayout.EAST);
jf.add(new JButton("中间"),BorderLayout.CENTER);
}
}
界面效果:
3)GridLayout:将容器划分为等大小的网格,每个网格中可以添加一个组件,所有组件大小相同
特点:适用于需要均匀排列组件的场景,行和列的数量可以指定
import javax.swing.*;
import java.awt.*;
public class GridLayoutDemo {
public static void main(String[] args) {
JFrame jf = new JFrame("网格布局");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(400,300);
jf.setVisible(true);
jf.setLayout(new GridLayout(3,2)); // 3行2列
jf.add(new JButton("1"));
jf.add(new JButton("2"));
jf.add(new JButton("3"));
jf.add(new JButton("4"));
jf.add(new JButton("5"));
jf.add(new JButton("6"));
}
}
界面效果:
4)BoxLayout:能够沿着单一轴线(X轴或Y轴)排列组件,可以创建水平(X轴)或垂直(Y轴)排列的布局
特点:适用于需要沿单一方向排列组件的场景,可以通过添加垂直或水平间隔(Glue、strut)来调整组件间距
public class BoxLayoutDemo {
public static void main(String[] args) {
JFrame jf = new JFrame("盒子布局");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(400,300);
jf.setVisible(true);
JPanel jp = new JPanel(); //创建面板(容器)
jp.setLayout(new BoxLayout(jp,BoxLayout.Y_AXIS)); // 垂直布局
//添加按钮和垂直间隔
jp.add(new JButton("登录"));
jp.add(Box.createVerticalStrut(10));
jp.add(new JButton("注册"));
jp.add(Box.createVerticalStrut(10));
jp.add(new JButton("忘记密码"));
jp.add(Box.createVerticalStrut(10));
jp.add(new JButton("帮助"));
jp.add(Box.createVerticalStrut(10));
//添加面板到窗口
jf.add(jp);
}
}
界面效果:
3.事件处理
GUI编程中,事件(点击按钮)的处理是通过事件监听器(EventListener)来完成的
常用的时间监听器对象:点击事件监听器 ActionListener、按键事件监听器 KeyListener、鼠标行为监听器 MouseListener
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class Test {
public static void main(String[] args) {
//创建登录窗口
JFrame jf = new JFrame("登录窗口");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setBounds(100,100,500,300);
jf.setVisible(true);
//创建登录按钮
JButton jb = new JButton("登录");
jb.setBounds(200,200,100,50);
//在窗口中添加登录按钮
jf.add(jb);
//给登录按钮添加监听器,点击时候执行代码
jb.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//点击按钮弹框显示登录成功
JOptionPane.showMessageDialog(jf,"登录成功!");
//弹框完成后设置主窗口为焦点接收键盘事件
jf.requestFocus();
}
});
//给窗口添加按键事件监听器,监听用户输入的上下左右按键
jf.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
//根据点击的键盘按键,判断用户输入的按键
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
System.out.println("用户按了上键!");
break;
case KeyEvent.VK_DOWN:
System.out.println("用户按了下键!");
break;
case KeyEvent.VK_LEFT:
System.out.println("用户按了左键!");
break;
case KeyEvent.VK_RIGHT:
System.out.println("用户按了右键!");
break;
default:
System.out.println("用户按下了非上下左右键!");
break;
}
}
});
}
}
4.事件的几种常用写法
第1种:直接使用匿名内部类的对象,代表事件监听对象,参考3.事件处理中的写法
第2种:直接提供实现类,用于创建事件监听对象
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Test2 {
public static void main(String[] args) {
//创建登录窗口
JFrame jf = new JFrame("登录窗口");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setBounds(100, 100, 500, 300);
jf.setVisible(true);
//创建登录按钮
JButton jb = new JButton("登录");
jb.setBounds(200, 200, 100, 50);
//在窗口中添加登录按钮
jf.add(jb);
//给登录按钮添加监听器,点击时候执行代码
jb.addActionListener(new AL(jf));
}
}
//创建点击按钮监听器的实现类
class AL implements ActionListener {
//创建成员变量
private JFrame jf;
//创建有参构造方法,传入JFrame对象
public AL (JFrame jf) {
this.jf = jf;
};
@Override
public void actionPerformed(ActionEvent e)
{
//弹窗提示登录成功
JOptionPane.showMessageDialog(jf, "登录成功!");
}
}
第3种:自定义窗口,让窗口对象实现事件接口(实际开发一个页面时,可为每个页面设计一个类,继承JFrame并实现ActionListener接口)
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
//自定义登录界面继承窗体类并实现ActionListener接口
public class LoginInterface extends JFrame implements ActionListener {
//创建登录窗口
public LoginInterface() {
setTitle("登录窗口");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 500, 300);
setVisible(true);
//创建登录按钮
JButton jb = new JButton("登录");
jb.setBounds(200, 200, 100, 50);
//初始化组件
init();
}
private void init() {
//创建面板
JPanel jp = new JPanel();
//创建登录按钮
JButton jb = new JButton("登录");
jb.setBounds(200, 200, 100, 50);
//在面板中添加登录按钮
jp.add(jb);
//在窗口中添加面板
add(jp);
//给登录按钮添加监听器,点击时候执行代码
jb.addActionListener(this);
}
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(this, "登录成功!");
}
}