Java
本笔记不包含运算、优先级、函数、类等基础知识。
面向对象三大特性:封装 继承 多态
封装
隐藏对象的属性和实现细节,仅对外公开访问方法,控制程序中属性的读和写的访问级别。
继承
在一个现有类的基础之上,增加新的方法或重写已有方法,从而产生一个新类。
关于继承如下3点:
- 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类的私有属性和方法子类是无法访问的,只是拥有
- 子类可以拥有自己属性和方法,既子类可以对父类进行扩展
- 子类可以用自己的方式实现父类的方法(重写)
多态
对象在不同时刻表现出来的不同状态。在编译时并不能确定,只有在运行期间才能决定
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,既一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定
Java 中实现多态的方式
- 继承:多个子类对同一方法的重写
- 接口:实现接口并覆盖接口的统一方法
类图
以下类图使用 PlantUMLopen in new window 绘制,更多语法及使用请参考: http://plantuml.com/open in new window 。
泛化关系 (Generalization)
用来描述继承关系,在 Java 中使用 extends 关键字。
实现关系 (Realization)
用来实现一个接口,在 Java 中使用 implement 关键字。
聚合关系 (Aggregation)
表示整体由部分组成,但是整体和部分不是强依赖的,整体不存在了部分还是会存在。
组合关系 (Composition)
和聚合不同,组合中整体和部分是强依赖的,整体不存在了部分也不存在了。比如公司和部门,公司没了部门就不存在了。但是公司和员工就属于聚合关系了,因为公司没了员工还在。
关联关系 (Association)
表示不同类对象之间有关联,这是一种静态关系,与运行过程的状态无关,在最开始就可以确定。因此也可以用 1 对 1、多对 1、多对多这种关联关系来表示。比如学生和学校就是一种关联关系,一个学校可以有很多学生,但是一个学生只属于一个学校,因此这是一种多对一的关系,在运行开始之前就可以确定。
依赖关系 (Dependency)
和关联关系不同的是,依赖关系是在运行过程中起作用的。A 类和 B 类是依赖关系主要有三种形式:
- A 类是 B 类中的(某中方法的)局部变量;
- A 类是 B 类方法当中的一个参数;
- A 类向 B 类发送消息,从而影响 B 类发生变化;
stream流
Optional容器
Optional是一个容器,它的成员变量是value,代表此容器中的元素。Optional可以存储任何类型的数据。
Optional的构造方法都是私有的,所以不能通过构造方法获取Optional对象。
可通过以下方法创建一个Optional对象:
- 使用
Optional.empty()
创建一个空容器。 - 使用
Optional.of(value)
创建一个值为value的容器。
Optional对象的成员方法:
get()
:返回容器的成员,若容器为空会抛出NoSuchElementException异常filter(Predicate<? super T> rule)
:判断容器存储的值是否符合判断规则,若符合则返回此对象,若不符合则返回空容器,参数是一个Predicate接口,该接口中只有一个抽象方法test用于比较要匹配的对象,返回布尔值,可以用lambda表达式引用其他已有方法isPresent()
:判断当前容器是否有元素,若有则返回true,否则返回falseorElse(T e)
:若容器为空则返回e,否则返回容器本身
package ClassStream;
import java.util.NoSuchElementException;
import java.util.Optional;
public class DemoOptional {
public static void main(String[] args) {
//此代码演示如何使用Optional类 Optional是一个容器类 并且对空指针异常做了优化 该类有泛型 因此可以保存任意类型的值
//Optional类不能使用new创建 需使用Optional类的三个静态方法创建实例
Optional<?> optional1 = Optional.empty(); //使用empty()方法创建存储空值的容器 此容器的值为null
try {
Object value = optional1.get();
System.out.println("容器1存储的值为" + value);
} catch (NoSuchElementException e) {
System.out.println("容器1是null值");
}
Optional<Double> optional2 = Optional.of(123.456); //使用of()方法创建实例 若此值是null则抛出空指针异常
//还可使用ofNullable()方法创建实例 使得此容器对象可以存储null值
//下面演示一些容器对象的常用方法
System.out.println("optional1是空值吗?" + optional1.isPresent()); //isPresent()方法可以判断该容器是否为null值
Double obj = optional2.get(); //使用get方法可以获得此容器存储的实例
Optional<Double> result = optional2.filter(e -> e > 123);
//使用filter()方法可判断容器存储的值是否符合判断规则 若符合则返回此对象 若不符合则返回空容器
System.out.println("optional2的元素:" + result.get());
//还有orElse(T t)方法 若此容器是null则返回参数值 若此容器不为空则返回此容器本身
}
}
运行结果:
容器1是null值
optional1是空值吗?false
optional2的元素:123.456
流的基本操作
将数据转换成流
因为流是接口,所以不能使用构造方法实例化。将数据转换成流的方法是使用集合Set或列表List及其子类的对象的成员方法stream() 实例化。 流中还分为两种操作:中间操作和终端操作。中间操作的返回值仍是一个流对象,可以对此对象继续使用流方法;终端操作的返回值是其他对象,该对象的类型不是流,因此不能继续进行流操作。
流对象的成员方法如下:
collect(Collector<? super T, A, R> collector)
:按照收集器规则将流元素重新封装成其他数据类型,比如列表、集合等,因为参数和返回值都是泛型,因此返回值是与参数类型一致的。参数我们一般选择Collectors类提供的toXXX()
方法,如想要封装成列表就写Collectors.toList()
。forEach(Consumer<? super T> action)
:遍历流,并对其中的所有元素都执行action方法。如我们想输出流的所有元素,就可以用stream.forEach(e -> System.out.println(e))
。filter(Predicate<? super T> predicate)
:将该对象中的数据按照参数中的规则筛选,返回值为新stream对象,其中保存的是符合条件的数据。distinct()
:将流中重复元素删除,返回一个没有重复元素的新流对象。limit(long MaxSize)
:取流中的MaxSize个元素,返回一个新流对象。skip(long n)
:去除流中的前n个元素,并返回一个新流对象。map(Function<? super T, ? extends R> mapper)
:按照mapper规则将流中的某一元素单独映射出来并且放在一个新流对象中返回。allMatch(Predicate<? super T> predicate)
:判断流中的元素是否都符合规则,若全部符合规则则返回true,否则返回false。anyMatch(Predicate<? superT> predicate)
:判断流中的元素是否都不符合规则,若全部不符合规则则返回true,否则返回false。noneMatch(Predicate<? super T> predicate)
:判断流中的元素是否有任一个符合规则,若有至少一个符合规则则返回true,否则返回false。collect(Collector<? super T, A, R> collector)
:按照收集器规则返回指定元素,详细介绍请见下面的介绍及示例代码。
流的几种使用场景如下:
- 过滤数组元素:使用
filter(rule)
方法过滤元素,其中参数rule需要的是一个可以返回布尔值的判断方法,返回值是全部符合条件的流对象。 - 去重:使用
distinct()
方法去除重复元素,该方法返回的是无重复元素的流对象,因此可以继续进行其他操作。 - 取流中的前n个元素或者去除前n个元素:使用
limit(n)
或者skip(n)
即可,返回值是包含前几个元素或去除前几个元素的流对象。 - 数据映射:使用
map(mapper)
将流中的某一具体元素提取出来,参数mapper
是映射规则,因为参数是Function类型,所以最好配合getter方法使用。如将有员工编号、性别、姓名、工资等的流中将姓名单独提取出来做成一个名单,就可以使用此方法。 - 数据校验:使用
allMatch(rule)
、anyMatch(rule)
、noneMatch(rule)
等方法判断元素符不符合规则,其中参数rule需要的是一个能够返回布尔值的判断方法,和上面的filter(rule)
类似,方法的介绍请看上面。 - 查找流中的最大和最小元素:使用流对象的
collect(Collectors.minBy(cmpRule))
实现。其中collect(rule)
表示按照rule方法将流中的元素封装成参数指定的参数对象,这里使用Collectors.minBy(rule)
和Collectors.maxBy(rule)
作为参数,Collectors是一个final类,它里面的每个方法都代表一个收集器规则。这里的这两个方法的返回值都是Optional容器,又因为使用了泛型,collect()
方法的返回值是和参数一致的,所以该方法的返回值也是Optional容器。参数rule是一个比较器对象,这里我们直接使用jdk已经提供好的比较器Comparator.comparing(element)
,参数element表示要比较什么对象。 - 分组:如按照部门分组,或先按照部门分组再按照性别分组。使用流对象的
collect(Collectors.groupingBy(element1,...))
实现。内部参数Collectors.groupingBy(element1,...)
表示按照元素element1或更多元素分组,若要进行多级分组就继续添加元素,配合getter方法使用。该方法的返回值是一个Map对象,所以collect()
的返回值也是Map对象,其中Map的键类型与element1的类型一致,对应的值类型是List类,List的泛型为该元素的类;若有多级分组,那么第一层的值类型就是Map对象,此Map对象的键类型与分组的第二个参数element2一致,以此类推。若要访问其中的元素,就可以按照访问List和Map对象的方法访问即可,实例请见StreamOperation3。
package ClassStream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamOperation1 {
public static void main(String[] args) {
//本文件演示流处理的第一类操作
//这里同时会一起介绍如何把数据集合转换成流
//将数据集合转换成流后可以对数据进行非常复杂的处理 下面演示如何使用流 演示的同时也会演示常用方法
//stream对象的filter()方法可以将该对象中的数据按照参数中的规则筛选,返回值为新stream对象,其中保存的是符合条件的数据
System.out.println("过滤--1.将所有年龄大于25岁的人列成名单:"); //过滤
List<Employee> list1 = Employee.getEmpList(); //先创建集合对象 可以是Set List和Map的子类 因为它们都实现了Collection接口
list1 = list1.stream() //先获取流对象 因为流是接口 所以只能通过这个方法创建流对象 创建流对象后才可以进行流操作
.filter(e -> e.getAge() > 25) //因为上一行代码返回的是流对象 因此可以对这个流对象进行操作 这里调用了filter方法 由于此方法是中间操作
//在操作后仍然是一个流对象 因此还可以调用其他流方法 此过滤方法的参数是一个lambda表达式 lambda表达式的参数会循环代替流中的元素
//lambda表达式的代码块是执行判断的 需要是能返回布尔值的表达式 这样符合条件的就会留在流中 不符合条件的会被删去
.collect(Collectors.toList()); //Collectors类提供的静态方法toList()方法可以将流中元素封装成list集合
// stream对象的collect对象可以返回参数指定类型的集合对象
System.out.println("过滤后的名单为:");
list1.forEach(System.out::println); //此forEach方法相当于增强的for循环 里面是一个lambda表达式 表达式的参数将会循环代替数组中的元素
//stream对象的distinct()方法可以去除流中的重复元素,返回值为新的stream对象
System.out.println("去重--删除所有重复的数字");
List<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
list2.add(4);
list2.add(3);//这里重复添加了一个3
System.out.print("原始数据:");
list2.forEach(e -> System.out.print(e + " "));
System.out.println();
list2 = list2.stream() //此对象是可变的数组对象 首先还是调用这个方法创建stream对象
.distinct() //去重 由于去重是中间操作 返回的还是一个stream对象 因此可以使用此对象继续执行其他操作
.collect(Collectors.toList()); //这里还是选择封装成数组
System.out.print("去重后数据:");
list2.forEach(e -> System.out.print(e + " "));
System.out.println();
//limit()方法会只取流中前N个元素,skip()方法则会跳过流中前N个元素 它们也是中间操作
System.out.println("limit()方法和skip()方法");
List<Employee> list3 = Employee.getEmpList();
System.out.println("limit()方法--取表中前3个人");
list3.stream().limit(3).collect(Collectors.toList()).forEach(System.out::println);
System.out.println("skip()方法--忽略表中前3个人");
list3.stream().skip(3).collect(Collectors.toList()).forEach(System.out::println);
}
}
运行结果:
过滤--1.将所有年龄大于25岁的人列成名单:
过滤后的名单为:
name=老张, age=40, salary=9000.0, sex=男, dept=运营部
name=大刚, age=32, salary=7500.0, sex=男, dept=销售部
name=翠花, age=28, salary=5500.0, sex=女, dept=销售部
name=老王, age=35, salary=6000.0, sex=女, dept=人事部
去重--删除所有重复的数字
原始数据:1 2 3 4 3
去重后数据:1 2 3 4
limit()方法和skip()方法
limit()方法--取表中前3个人
name=老张, age=40, salary=9000.0, sex=男, dept=运营部
name=小刘, age=24, salary=5000.0, sex=女, dept=开发部
name=大刚, age=32, salary=7500.0, sex=男, dept=销售部
skip()方法--忽略表中前3个人
name=翠花, age=28, salary=5500.0, sex=女, dept=销售部
name=小马, age=21, salary=3000.0, sex=男, dept=开发部
name=老王, age=35, salary=6000.0, sex=女, dept=人事部
name=小王, age=21, salary=3000.0, sex=女, dept=人事部
package ClassStream;
import java.util.List;
import java.util.stream.Collectors;
public class StreamOperation2 {
public static void main(String[] args) {
List<Employee> list = Employee.getEmpList();
//数据映射--map()方法可以将流中数据按大多数照参数的逻辑映射出来 不要和封装成Map对象的方法混淆了
System.out.println("映射--将所有的人名提取出来");
List<String> list1 = list.stream().map(Employee::getName) //map()的参数是一个具体的字段 返回值就是所有该字段元素组成的流对象
.collect(Collectors.toList()); //由于是流对象 我们依然可以将它当成流对象操作它 也可以封装成数组或集合
System.out.print("将所有的人名提取出来的结果:");
list1.forEach(e -> System.out.print(e + " "));
System.out.println();
//数据查找
//allMatch()方法会判断流中的数据是否全部符合条件,若是返回true,有一个不符合就返回false
System.out.println("员工都大于40岁吗? " + list.stream().allMatch(e -> e.getAge() > 40));
//anyMatch()方法会判断流中的数据是否至少有一个符合条件,若是返回true,都不符合就返回false
System.out.println("员工有25岁以下的吗? " + list.stream().anyMatch(e -> e.getAge() < 25));
//noneMatch()方法会判断流中的数据是否全部不符合条件,若是返回true,有一个不符合就返回false
System.out.println("员工的薪资都不低于2000元吗? " + list.stream().noneMatch(e -> e.getSalary() < 2000));
}
}
运行结果:
映射--将所有的人名提取出来
将所有的人名提取出来的结果:老张 小刘 大刚 翠花 小马 老王 小王
员工都大于40岁吗? false
员工有25岁以下的吗? true
员工的薪资都不低于2000元吗? true
package ClassStream;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class StreamOperation3 {
public static void main(String[] args) {
List<Employee> list = Employee.getEmpList();
//数据统计
//注: 以下的所有方法都是流对象的成员方法 有些方法的操作为中间操作,返回的仍然可能为流对象
System.out.println("员工总人数:" + list.stream().count());
//count()方法返回当前流的元素个数
Optional<Employee> op1 = list.stream().collect(Collectors.minBy(Comparator.comparing(Employee::getAge))); //按照比较器规则返回最小值
System.out.println("公司年龄最小的员工为:" + op1.get());
Optional<Employee> op2 = list.stream().collect(Collectors.maxBy(Comparator.comparing(Employee::getAge)));
System.out.println("公司年龄最大的员工为:" + op2.get());
Double avgSalary = list.stream().collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println("公司薪资的平均值为:" + avgSalary);
Double sumSalary = list.stream().collect(Collectors.summingDouble(Employee::getSalary));
System.out.println("公司薪资的总和为:" + sumSalary);
String nameList = list.stream().map(Employee::getName).collect(Collectors.joining(","));
System.out.println("公司人员名单:" + nameList);
//数据分组
//一级分组
//方法返回值是一个Map集合 键是分组的规则 对应的元素就是分组后的结果
Map<String, List<Employee>> map1 = list.stream().collect(Collectors.groupingBy(Employee::getDept));
for (String name : map1.keySet()) {
System.out.println("[" + name + "]的员工列表如下:");
map1.get(name).stream().forEach(e -> System.out.println("\t" + e));
}
//二级分组:先按照部门分组,再按性别分组
//groupingBy()方法的重载形式有第二个参数 第二个参数就是第二个分组规则 这个函数的重载是递归的 所以可以看到第二次分组又做了第一次分组的参数
//这样分组的返回值就是一个Map对象 第一个Map对象的键就是第一次分组的规则 每个键对应的值又是一个新的Map对象 这个Map对象的键就是第二次分组的规则 元素就是最后的元素了
Map<String, Map<String, List<Employee>>> map2 = list.stream().collect(Collectors.groupingBy(Employee::getDept, Collectors.groupingBy(Employee::getSex)));
for (String name1 : map1.keySet()) {
System.out.println("[" + name1 + "]");
for (String name2 : map2.get(name1).keySet()) {
System.out.println("\t" + name2 + "性的员工列表如下:");
map2.get(name1).get(name2).stream().forEach(e -> System.out.println("\t\t" + e));
}
}
}
}
运行结果:
员工总人数:7
公司年龄最小的员工为:name=小马, age=21, salary=3000.0, sex=男, dept=开发部
公司年龄最大的员工为:name=老张, age=40, salary=9000.0, sex=男, dept=运营部
公司薪资的平均值为:5571.428571428572
公司薪资的总和为:39000.0
公司人员名单:老张,小刘,大刚,翠花,小马,老王,小王
[销售部]的员工列表如下:
name=大刚, age=32, salary=7500.0, sex=男, dept=销售部
name=翠花, age=28, salary=5500.0, sex=女, dept=销售部
[人事部]的员工列表如下:
name=老王, age=35, salary=6000.0, sex=女, dept=人事部
name=小王, age=21, salary=3000.0, sex=女, dept=人事部
[开发部]的员工列表如下:
name=小刘, age=24, salary=5000.0, sex=女, dept=开发部
name=小马, age=21, salary=3000.0, sex=男, dept=开发部
[运营部]的员工列表如下:
name=老张, age=40, salary=9000.0, sex=男, dept=运营部
[销售部]
女性的员工列表如下:
name=翠花, age=28, salary=5500.0, sex=女, dept=销售部
男性的员工列表如下:
name=大刚, age=32, salary=7500.0, sex=男, dept=销售部
[人事部]
女性的员工列表如下:
name=老王, age=35, salary=6000.0, sex=女, dept=人事部
name=小王, age=21, salary=3000.0, sex=女, dept=人事部
[开发部]
女性的员工列表如下:
name=小刘, age=24, salary=5000.0, sex=女, dept=开发部
男性的员工列表如下:
name=小马, age=21, salary=3000.0, sex=男, dept=开发部
[运营部]
男性的员工列表如下:
name=老张, age=40, salary=9000.0, sex=男, dept=运营部
集合类
集合类包含Set集合、List列表、Map字典三个接口。
Collection接口
上述三个接口都实现了Collection接口,该接口使用实现类实例化,如ArrayList。该接口的对象有以下几个常用成员方法:
isEmpty()
:判断收集器是否为空,若为空则返回true,否则返回false。add(E e)
:将元素e添加到收集器中。该方法有一个返回值,若成功添加则返回true,否则返回false。size()
: 返回收集器的元素个数。
package CollectionClasses;
import java.util.ArrayList;
import java.util.Collection;
// Iterator的方法如下:
// boolean hasNext():如果被迭代的集合元素还没有被遍历完,则返回 true。
// Object next():返回集合里的下一个元素。
// void remove():删除集合里上一次 next 方法返回的元素。
public class InterfaceCollection {
public static void main(String[] args) {
//本文件演示Collection接口的使用
//因为List Map Set类都是Collection的实现类 因此没有必要使用Collection做实例化对象的类型 使用前三个类即可
//典型的创建接口用实现类实例化
Collection<Object> list = new ArrayList<>();
System.out.println("添加元素之前list集合现在是空的吗? " + list.isEmpty());
list.add(new Object()); //添加一个Object对象
list.add("String"); //添加一个字面值为"String"的字符串
list.add(Math.PI); //添加一个双精度浮点值
System.out.println("list集合现在有几个元素:" + list.size());
System.out.println("添加元素之后list集合现在是空的吗? " + list.isEmpty());
//遍历list
for (Object o : list) {
System.out.println(o);
}
// 本文件仅展示怎么遍历Collection中的元素
}
}
运行结果:
添加元素之前list集合现在是空的吗? true
list集合现在有几个元素:3
添加元素之后list集合现在是空的吗? false
java.lang.Object@7f31245a
String
3.141592653589793
列表——List
ArrayList
ArrayList是List的一个实现类,该类实现了可变的数组,允许保存所有元素,包括null,可以是用索引位置对集合进行快速访问,缺点是向指定的索引位置添加或者删除对象的速度较慢。
ArrayList伴随一个泛型E,指元素的数据类型。
ArrayList可以使用增强的for循环遍历。
ArrayList类的成员方法如下:
isEmpty()
:判断数组是否为空,若为空则返回true,否则返回false。add(E e)
:向数组中添加元素,返回一个布尔值,若成功添加则返回true,否则返回false。size()
: 返回数组的元素个数。remove(int n)
: 将第n个元素移除。set(int n, T e)
: 将第n个元素修改为e。
package CollectionClasses;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class ListCollection {
public static void main(String[] args) {
//本文件演示怎么使用List类
//List是一个接口 需要使用实现类实例化
//list集合的两个实现类分别为ArrayList和LinkedList
//ArrayList类实现了可变的数组,允许保存所有元素,包括null,可以使用索引位置对集合进行快速访问
//缺点是向指定的索引位置添加或者删除对象的速度较慢
ArrayList<Object> list1 = new ArrayList<>(); //典型的向上转型
System.out.println("添加元素之前list1现在是不是空集合?" + list1.isEmpty()); //isEmpty()方法判断数组是不是空的
list1.add("蔡徐坤"); //add()方法向数组中添加元素
list1.add("王德发");
list1.add(null);
list1.add(Math.E);
list1.add('A');
System.out.println("添加元素之后list1现在是不是空集合?" + list1.isEmpty());
System.out.println("现在list1的元素个数为:" + list1.size()); //size()方法返回数组内元素的个数
System.out.println("第一次遍历list1"); //遍历元素
//遍历链表
for (Object o : list1) {
System.out.println(o);
}
//下面对集合的元素进行调整
list1.remove(0); //移除第一个元素
list1.set(1, Math.PI); //将第二个元素修改为PI
System.out.println("现在list1的元素个数为:" + list1.size());
System.out.println("第二次遍历list1"); //再次遍历
for (Object o : list1) {
System.out.println(o);
}
System.out.println("-----------------------");
//LinkedList类采用链表形式保存对象
//这种结构的优点是便于向集合中插入或删除对象.但是不便于访问对象,效率较低.
List<Object> list2 = new LinkedList<>();
list2.add("蔡徐坤");
list2.add("鹿晗");
list2.add("123456");
list2.add(3.14);
System.out.println("list2的元素个数为:" + list2.size());
System.out.println("第一次遍历list2");
for (Object obj : list2) { //注意这里使用了增强的for循环便利链表 其实数组也可以
System.out.println(obj);
}
System.out.println("取出第1个元素:" + list2.get(0));
list2.remove("鹿晗");
list2.set(1, "456789");
System.out.println("第二次遍历list2");
for (Object obj : list2) {
System.out.println(obj);
}
}
}
运行结果:
添加元素之前list1现在是不是空集合?true
添加元素之后list1现在是不是空集合?false
现在list1的元素个数为:5
第一次遍历list1
蔡徐坤
王德发
null
2.718281828459045
A
现在list1的元素个数为:4
第二次遍历list1
王德发
3.141592653589793
2.718281828459045
A
-----------------------
list2的元素个数为:4
第一次遍历list2
蔡徐坤
鹿晗
123456
3.14
取出第1个元素:蔡徐坤
第二次遍历list2
蔡徐坤
456789
3.14
LinkedList
LinkedList类采用链表形式保存对象,这种结构的优点是便于向集合中插入或删除对象,但是不便于访问对象,效率较低。
LinkedList的成员方法与ArrayList类似,这两种存储结构由于都是List的子类,因而方法很相似。这里不再演示。
集合——Set
HashSet
HashSet实现了Set接口,由哈希表支持。它不保证Set集合的迭代顺序,特别是它不保证该顺序恒久不变。该类可以使用null。
HashSet类的成员方法:
add(E e)
:若集合中不存在元素e则添加元素e,若成功添加返回true,否则返回false。remove(Object o)
:移除元素o,若成功移除则返回true,否则返回false。size()
:返回集合中的元素个数。isEmpty()
:判断集合是否为空,若为空则返回true,否则返回false。
public class DemoHashSet {
public static void main(String[] args) {
//HashSet类实现Set接口,由哈希表支持
//它不保证Set集合的迭代顺序,特别是它不保证该顺序恒久不变
//该类可以使用null
//注:在Person1类中 HashCode()方法被重写了 其仅有id的哈希值相同时就返回true
HashSet<Person1> set = new HashSet<>();
set.add(new Person1(1, "小明"));
set.add(new Person1(2, "小明")); //这个对象的名字虽然和上一个对象的名字相等 但是id的哈希值不同 因此认定是不同的人
set.add(new Person1(1, "小张")); //因为这个对象的哈希值和第一个对象的相同 不会添加
System.out.println("set集合的长度是: " + set.size()); //size()方法可以获取集合中元素的个数
System.out.print("set集合中的元素是: ");
for (Object obj : set) {
System.out.print(obj + " "); //注意看结果
}
}
}
运行结果:
set集合的长度是: 2
set集合中的元素是: Person1 [ id=1, name = 小明 ] Person1 [ id=2, name = 小明 ]
TreeSet
TreeSet类不仅实现了Set接口,还实现了Java.util.SortSet接口,因此TreeSet类实现的Set集合在遍历集合时按照自然顺序递增排列,也可以按照比较器递增排序。
TreeSet和HashSet的成员方法一致,这里就不赘述了。
字典Map
字典也可以称为映射,字典在初始化时可以指定两个泛型,第一个泛型指键的类型,第二个泛型指值的泛型,由于映射关系中一个键对应一个值,因此必须保证键的唯一性。
HashMap
HashMap基于哈希表,允许使用null值和null键,但必须保证键的唯一性。
HashMap的成员方法:
put(K key, V value)
:向字典中添加键为key和值为value的映射关系。该方法的返回值为value。size()
:返回字典中存在的映射关系数量。keySet()
:返回字典中所有键组成的集合。get(Object key)
:返回键为key对应的值元素。remove(K key)
:移除键为key的映射关系。
TreeMap的映射具有一定的顺序,但是读写性能较差,也不能使用null,所以我们一般使用HashMap。
package CollectionClasses;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapCollection {
public static void main(String[] args) {
//这个文件演示了映射的使用
//Map接口的两个实现了分别是HashMap和TreeMap
//HashMap基于哈希表 允许使用null值和null键 但必须保证键的唯一性
//TreeMap的映射有一定的顺序 不可以使用null 性能稍差 建议平时使用时使用HashMap
Map<Object, Object> map = new HashMap<>();
map.put("String", "这是一个字符串"); //put()方法用于添加元素 第一个参数是键 第二个参数是要保存的对象
map.put("Object", new Object());
map.put("int", 1234);
map.put("数字", 1234);
map.put(1, "1");
map.put(null, null);
System.out.println("map中的元素个数:" + map.size()); //size()方法返回此对象元素个数
Set<Object> set = map.keySet(); //keySet方法可以返回映射中所有的键集
for (Object obj : set) {
System.out.println("key = " + obj + " value = " + map.get(obj)); //get()根据键值返回对应元素
}
}
}
运行结果:
key = null value = null
key = 1 value = 1
key = Object value = java.lang.Object@7f31245a
key = String value = 这是一个字符串
key = 数字 value = 1234
key = int value = 1234
日期时间类
Date类
Date类的构造方法
public Date()
:创建一个当前时间戳的日期对象。public Date(long when)
:创建一个时间戳为when的时间对象。public Date(int year, int month, int date)
:创建一个日期为year年month月date日的日期对象。需要注意month是从0开始的,也就是说若要创建一个月份为1的日期对象需要使用0作为参数,此处我们也可以用Calendar类的月份常量代替。此方法已被弃用。public Date(int year, int month, int date, int hrs, int min)
:创建一个日期为year年month月date日hrs小时min分的日期对象,month月份的规则同上,此方法已被弃用。public Date(int year, int month, int date, int hrs, int min, int sec)
:创建一个日期为year年month月date日hrs小时min分sec秒的日期对象,month月份的规则同上,此方法已被弃用。
Date类的成员方法
after(Date when)
:判断当前日期是否在指定日期之后。before(Date when)
:判断当前日期是否在指定日期之前。
时间格式化
使用DateFormat类
DateFormat是抽象类,不能使用new实例化,可以根据需要选用的时间格式选择以下四种方法中的一种。
getInstance()
getDateInstance()
getTimeInstance()
getDateTimeInstance()
DateFormat的格式化风格有以下几种:SHORT、MEDIUM、LONG、FULL。它们可以作为以上四种实例化方法的参数,输出对应格式的时间。
在实例化DateFormat对象后,调用对象的format(date)
方法即可,其中date是Date的对象。
public class ClassDateFormat {
public static void main(String[] args) {
//DateFormat类是抽象类,不能用new实例化
//DateFormat类的格式化风格有SHORT,MEDIUM,LONG,FULL四种
//DateFormat类要想创建对象,可以调用以下方法:
//getInstance() getDateInstance() getTimeInstance() getDateTimeInstance()
//下面给几种DateFormat类实例化的例子
//注:使用sout方法输出时,要用该对象的format(Date date)方法将它转换为字符串
DateFormat df1 = DateFormat.getInstance();
System.out.println(df1.format(new Date())); //按照默认格式输出
DateFormat df2 = DateFormat.getTimeInstance(DateFormat.LONG);
System.out.println(df2.format(new Date())); //输出长类型格式的时间
DateFormat df3 = DateFormat.getDateInstance(DateFormat.LONG);
System.out.println(df3.format(new Date())); //输出常量类型的日期
DateFormat df4 = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG);
System.out.println(df4.format(new Date())); //输出长类型的日期和时间
}
}
运行结果:
23-4-19 下午6:57
下午06时57分23秒
2023年4月19日
2023年4月19日 下午06时57分23秒
使用SimpleDateFormat类
SimpleDateFormat继承自DateFormat类,可以使用向上转型实例化此对象,使用该类可以按照我们想要的格式格式化时间。
SimpleDateFormat类的构造方法:
public SimpleDateFormat(String pattern)
:按照pattern指定的模式格式化时间,其中每个子字符串所代表的意义如下:
符号 | 作用 |
---|---|
yyyy | 年份 |
MM | 月份 |
dd | 一个月中的某一天 |
D | 一年中的某一天 |
HH | 一天中的某个小时 |
mm | 一小时中的某一分钟 |
ss | 一分钟的某一秒 |
E | 星期 |
a | 上午或下午 |
在实例化SimpleDateFormat对象后,使用此对象的format(date)
方法就可以格式化日期,参数date为Date的实例化对象。format()
方法会返回一个字符串,表示格式化后的时间。
package FrequentClasses.ClassDateAndTime;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ClassSimpleDateFormat {
public static void main(String[] args) {
//常用的时间格式:
//2021/10/25 yyyy/MM/dd
//2021.10.25 yyyy.MM.dd
//2021-10-25 00:11:22 星期日 yyyy-MM-dd HH:mm:ss EEEE
//下午3时 ah时
//今年已经过了256天 今年已经过了D天
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日 EEEE HH时mm分ss秒");
System.out.print("各位观众大家好,现在是");
System.out.print(df.format(new Date()));
System.out.println(",欢迎收看新闻。");
}
}
运行结果:
各位观众大家好,现在是2023年04月19日 星期三 18时57分52秒,欢迎收看新闻。
Calendar类
因为Date类在设计之初没有考虑到国际化,且很多方法都已经过时了,因此JDK提供了新的时间处理类:Calendar类。
Calender类是抽象类,因此需要调用Calendar.getInstance()
方法获取其对象。
Calendar类的成员方法:
getTime()
:返回一个字面值为当前日历对象所表示的时间的日期对象。set(int year, int month, int date, int hourOfDay, int minute, int second)
:设置当前日历对象所表示的时间。after(Object when)
:判断当前日历所表示的时间是否在when所表示的时间后面。若符合则返回true,否则返回false。before(Object when)
:判断当前日历所表示的时间是否在when所表示的时间前面。若符合则返回true,否则返回false。add(int field, int amount)
:将当前日历对象所表示的时间的一个字段加上一个时间量。field表示该时间如何表示。如c.add(Calendar.DAY_OF_MONTH, 30)
:表示加上30天。get(int field)
获取日历对象的某一字段,获取对应的时间值。
package FrequentClasses.ClassDateAndTime;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class ClassCalendar {
public static void main(String[] args) {
//因为Date类在设计之初没有考虑到国际化 且很多方法都已经过时了 因此JDK提供了新的时间处理类 Calendar类
//Calender类是抽象类 因此需要调用Calendar.getInstance()方法获取其对象
Calendar calendar1 = Calendar.getInstance(); //默认的创建日历对象的方法
Date date = calendar1.getTime(); //getTime()方法返回一个字面值为当前日历对象所表示的时间的日期对象
System.out.println("使用日历实例化的日期对象所表示的时间是:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
Calendar calendar2 = Calendar.getInstance();
calendar2.set(2020, Calendar.OCTOBER, 15, 12, 12, 14);
System.out.println("calender2所表示的时间为:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar2.getTime()));
System.out.println("calender1在calender2后面吗?" + calendar1.after(calendar2));
System.out.println("calender1在calender2前面吗?" + calendar1.before(calendar2));
calendar2.add(Calendar.DAY_OF_MONTH, 30); //add()函数可以给日历对象的一个字段加减时间量 这里把calender2的日加了30天
System.out.println("calender2加了30天后所表示的时间为:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar2.getTime()));
//注:Calendar.MONTH的第一个月使用0记录的 因此要获得月份数字的话需要在后面加1
//add(Calender.DAY_PF_MONTH, 0)获取的要把月份是上个月的最后一天 所以在调用时需加1
//Calendar.DAY_OF_WEEK的第一天是周日 要注意
int day = calendar2.get(Calendar.DAY_OF_MONTH); //get()方法可以获得指定字段所表示的时间值
System.out.println("calender2中该天在一个月中的位置为第" + day + "天");
}
}
运行结果:
使用日历实例化的日期对象所表示的时间是:2023-04-19 18:58:21
calender2所表示的时间为:2020-10-15 12:12:14
calender1在calender2后面吗?true
calender1在calender2前面吗?false
calender2加了30天后所表示的时间为:2020-11-14 12:12:14
calender2中该天在一个月中的位置为第14天
常用类
BigInteger类
BigInteger是一个支持任意精度、任意长度的大整数类,在涉及到数字非常大且要求精度非常准确的情况下就可以使用大整数类,该类使用字符串保存整数。
BigInteger类的构造方法:
BigInteger(String val)
:创建一个数字大小为val的大整数对象。
BigInteger类的成员方法:
add(BigInteger val)
:将此BigInteger的值加上val后返回结果表示的BigInteger对象。subtract(BigInteger val)
:将此BigInteger的值减去val后返回结果表示的BigInteger对象。multiply(BigInteger val)
将两个BigInteger表示的值相乘之后返回结果表示的BigInteger对象。divide(BigInteger val)
:返回两个BigInteger相除的商divideAndRemainder(BigInteger val)
:返回一个数组,第一个元素表示两个BigInteger相除的商,第二个元素表示两个BigInteger相除后的余数。pow(int exponent)
:乘方运算,exponent表示指数。negate()
:取反运算。
BigInteger还有很多数学方法,这里就不一一列举了。
BigDecimal类
BigDecimal是大小数类,该类也同样支持任意精度和任意大小的小数,且没有误差。BigDecimal类的构造方法如下:
- BigDecimal(String val) 创建一个值相当于val的BigDecimal对象。
BigDecimal类的成员方法如下:
add(BigDecimal augend)
:将两个BigDecimal相加后返回新的BigDecimal。subtract(BigInteger val)
:将此BigDecimal对象所表示的实数值减去val后返回一个新的BigDecimal。multiply(BigInteger val)
:将两个BigDecimal对象相乘后返回值为积的BigDecimal对象。divideAndRemainder(BigInteger val)
:返回一个数组,第一个元素是两个BigDecimal相除的商,第二个元素是两个BigDecimal相除的余数。divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
:将两个BigDecimal对象相除后返回值为商的BigDecimal对象。其中divisor表示除数,scale表示商的小数保留位数,roundingMode表示最后一位数字的取舍,可选以下几种:
符号 | 意义 |
---|---|
RoundingMode.UP | 商的最后一位大于0则向前进位 否则向后进位 |
RoundingMode.DOWN | 舍弃商的最后一位 |
RoundingMode.CEILING | 若商大于0则按照UP处理 若商小于0则按照DOWN处理 |
RoundingMode.FLOOR | 若商小于0则按照DOWN处理 若商大于0则按照UP处理 |
RoundingMode.HALF_DOWN | 四舍五入 若最后一位小于等于5则舍弃 若大于5则进位 |
RoundingMode.HALF_UP | 四舍五入 若最后一位小于5则舍弃 若大于等于5则进位 |
RoundingMode.HALF_EVEN | 若商的倒数第二位为奇数则按UP处理 若商的倒数第二位是偶数则按DOWN处理 |
注意:由于大小数的相除十分复杂,要考虑除不尽的问题,所以我们要考虑小数取舍的问题。
pow(int n)
乘方运算。negative()
取相反数运算。
package FrequentClasses.UtilClasses;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
public class ClassBigIntegerAndBigDecimal {
public static void main(String[] args) {
//下方演示怎么使用大整数类 该类可以存储任意精度的整数值
BigInteger bigInteger1 = new BigInteger("987654321123456789009876543210");
BigInteger bigInteger2 = new BigInteger("123456789009876543211234567890");
//大整数可以进行加减乘除等基本运算
System.out.println(bigInteger1 + " + " + bigInteger2 + " = " + (bigInteger1.add(bigInteger2)));
System.out.println(bigInteger1 + " - " + bigInteger2 + " = " + (bigInteger1.subtract(bigInteger2)));
System.out.println(bigInteger1 + " × " + bigInteger2 + " = " + (bigInteger1.multiply(bigInteger2)));
System.out.println(bigInteger1 + " ÷ " + bigInteger2 + " = " + (bigInteger1.divide(bigInteger2)));
//大整数可以进行求余运算 返回一个数组 第一个是商 第二个是余数
System.out.println(bigInteger1 + " 除以 " + bigInteger2 + " 的余数是 " + bigInteger1.divideAndRemainder(bigInteger2)[1]);
//大整数可以进行乘方和取相反数运算
System.out.println(bigInteger1 + " 的平方是 " + bigInteger1.pow(2));
System.out.println(bigInteger1 + " 的相反数是 " + bigInteger1.negate());
//下方演示怎么使用大小数类 该类可以存储任意精度的小数值
BigDecimal bigDecimal1 = new BigDecimal("1234567890.0987654321");
BigDecimal bigDecimal2 = new BigDecimal("9876543210.0123456789");
//大整数可以进行加减乘除等基本运算
System.out.println(bigDecimal1 + " + " + bigDecimal2 + " = " + (bigDecimal1.add(bigDecimal2)));
System.out.println(bigDecimal1 + " - " + bigDecimal2 + " = " + (bigDecimal1.subtract(bigDecimal2)));
System.out.println(bigDecimal1 + " × " + bigDecimal2 + " = " + (bigDecimal1.multiply(bigDecimal2)));
//注意 由于大小数的除法很复杂 因为在除不尽的话需要考虑小数点最后的数字取舍情况 因此一般使用除法时会指定小数保留位数和取舍规则
//在该方法中 第一个参数是除数 第二个参数是保留的小数位数 第三个参数是取舍规则
//常用取舍规则
//RoundingMode.UP 商的最后一位大于0则向前进位 否则向后进位
//RoundingMode.DOWN 舍弃商的最后一位
//RoundingMode.CEILING 若商大于0则按照UP处理 若商小于0则按照DOWN处理
//RoundingMode.FLOOR 若商小于0则按照DOWN处理 若商大于0则按照UP处理
//RoundingMode.HALF_DOWN 四舍五入 若最后一位小于等于5则舍弃 若大于5则进位
//RoundingMode.HALF_UP 四舍五入 若最后一位小于5则舍弃 若大于等于5则进位
//RoundingMode.HALF_EVEN 若商的倒数第二位为奇数则按UP处理 若商的倒数第二位是偶数则按DOWN处理
System.out.println(bigDecimal1 + " ÷ " + bigDecimal1 + " = " + (bigDecimal1.divide(bigDecimal2, 50,
RoundingMode.DOWN)));
//大小数可以进行求余运算 返回一个数组 第一个是商 第二个是余数
System.out.println(bigDecimal1 + " 除以 " + bigDecimal2 + " 的余数是 " + bigDecimal1.divideAndRemainder(bigDecimal2)[1]);
//大小数可以进行乘方和取相反数运算
System.out.println(bigDecimal1 + " 的平方是 " + bigDecimal1.pow(2));
System.out.println(bigDecimal1 + " 的相反数是 " + bigDecimal1.negate());
}
}
运行结果:
987654321123456789009876543210 + 123456789009876543211234567890 = 1111111110133333332221111111100
987654321123456789009876543210 - 123456789009876543211234567890 = 864197532113580245798641975320
987654321123456789009876543210 × 123456789009876543211234567890 = 121932631137631458331748210343629019963293095577211263526900
987654321123456789009876543210 ÷ 123456789009876543211234567890 = 8
987654321123456789009876543210 除以 123456789009876543211234567890 的余数是 9044444443320000000090
987654321123456789009876543210 的平方是 975461058033836303260021337908428594442350251485778997104100
987654321123456789009876543210 的相反数是 -987654321123456789009876543210
1234567890.0987654321 + 9876543210.0123456789 = 11111111100.1111111110
1234567890.0987654321 - 9876543210.0123456789 = -8641975319.9135802468
1234567890.0987654321 × 9876543210.0123456789 = 12193263112254229536.54138088831112635269
1234567890.0987654321 ÷ 1234567890.0987654321 = 0.12499999887078125001707363279942037426795218242690
1234567890.0987654321 除以 9876543210.0123456789 的余数是 1234567890.0987654321
1234567890.0987654321 的平方是 1524157875262917362.23502514857789971041
1234567890.0987654321 的相反数是 -1234567890.0987654321
DecimalFormat类
DecimalFormat类是一个数字格式化类,该类的构造方法如下:
DecimalFormat(String pattern)
:其中pattern代表格式字符串,各个子字符串的意义如下表:
符号 | 意义 |
---|---|
0 | 数字占位符,若该位存在数字则显示该数字,若该位不存在数字则显示0 |
# | 数字占位符,若该位存在数字则显示数字,若不存在数字则不显示任何字符 |
. | 小数点 |
- | 负号 |
% | 百分号 |
\u2030 | 千分号 |
E | 分割科学计数法的小数和尾数 |
' | 单引号,转义字符 |
DecimalFormat类的成员方法:
format(double number)
:按照此DecimalFormat定义时的格式字符串格式化number并返回为字符串。
package FrequentClasses.UtilClasses;
import java.text.DecimalFormat;
public class CLassDecimalFormat {
//改文件演示如何使用数字格式化
//DecimalFormat类是NumberFormat类的一个子类 提供为十进制数字格式化的格式
public static StringBuilder ApplyPattern(String pattern, double value) {
DecimalFormat decimalFormat = new DecimalFormat(pattern); //该构造方法以一个字符串为参数 该字符串即为要格式化的格式
//# 和 0 都是数字占位符 . - , % \u2030(千分号) 都是它们本来的含义 \u00A4 货币记号 E 分割科学计数法的小数和尾数 '单引号 转义字符
StringBuilder result = new StringBuilder();
result.append("原数字: ");
result.append(value);
result.append(" 格式: ");
result.append(pattern);
result.append(" 结果: ");
result.append(decimalFormat.format(value));
return result;
}
public static void main(String[] args) {
System.out.println(ApplyPattern("####.####",123456.123456));
System.out.println(ApplyPattern("0000.0000",123.321));
System.out.println(ApplyPattern("#%",0.8));
System.out.println(ApplyPattern("#\u2030",0.8));
System.out.println(ApplyPattern("###,###",1234567890));
System.out.println(ApplyPattern("####,####",1234567890));
}
}
运行结果:
原数字: 123456.123456 格式: ####.#### 结果: 123456.1235
原数字: 123.321 格式: 0000.0000 结果: 0123.3210
原数字: 0.8 格式: #% 结果: 80%
原数字: 0.8 格式: #‰ 结果: 800‰
原数字: 1.23456789E9 格式: ###,### 结果: 1,234,567,890
原数字: 1.23456789E9 格式: ####,#### 结果: 12,3456,7890
Random类
Random是随机数类,此类提供了许多生成伪随机数的成员方法。若要获取随机数,需要显示使用new Random()
获取一个生成器对象,然后调用该对象的下面的成员方法就可以获取对应的随机数。同时Random类的构造方法还有一个重载形式:Random(long seed),即指定种子。
nextInt()
:返回一个随机整数。nextInt(int bound)
:返回一个0到bound的随机整数。nextFloat()
:返回一个随机单精度浮点值。nextDouble()
:返回一个随机双精度浮点值。nextBoolean()
:返回一个随机布尔值。
package FrequentClasses.UtilClasses;
import java.util.Random;
public class ClassRandom {
public static void main(String[] args) {
//本文件演示Random类的使用
Random random = new Random(); //可以指定参数 参数为随机数种子
System.out.println("随机产生一个整数:" + random.nextInt());
System.out.println("随机产生一个0-10间的整数:" + random.nextInt(10)); //返回一个0-10之间的整数
System.out.println("随机产生一个单精度浮点数:" + random.nextFloat());
System.out.println("随机产生一个双精度浮点数:" + random.nextDouble());
System.out.println("随机产生一个布尔值:" + random.nextBoolean());
}
}
运行结果:
随机产生一个整数:-657668008
随机产生一个0-10间的整数:8
随机产生一个单精度浮点数:0.627032
随机产生一个双精度浮点数:0.9477657643117339
随机产生一个布尔值:true
System类
System类含有很多与系统有关的静态方法和静态对象,如System.in,System.out等。
System类的常用方法:
System.currentTimeMillis()
:获取当前系统时间的毫秒数。System.exit(int status)
:以状态码status结束此进程。
IO流
File类
File是文件类,提供Java程序到文件的通道,使用File可以对文件进行操作。
File类的构造方法:
File(String pathname)
:pathname指文件名。以文件名实例化File对象。File(String parent, String child)
:parent指父文件夹,child指子文件或子文件夹。以父文件夹和子文件夹的组合路径实例化File对象。
File类的成员方法:
exists()
:若文件存在,返回true,否则返回false。delete()
:删除此文件。若成功删除返回true,否则返回false。createNewFile()
:创建文件。若创建成功返回true,否则返回false。若路径不存在或拒绝访问会抛出IOException异常。getName()
:返回文件的名称。length()
:返回文件的大小,单位是字节数。lastModified()
:返回文件最后编辑的毫秒数。isHidden()
:若文件处于隐藏状态则返回true,否则返回false。isDirectory()
:判断此文件对象的操作对象是否是文件夹,若是则返回true,否则返回false。isFile()
:判断此文件对象的操作对象是否是文件,若是返回true,否则返回false。
在使用完文件之后要及时关闭文件。
package IO;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class ClassFile {
public static void main(String[] args) {
//File类的三种构造方法
File file1 = new File("word.txt"); //File(文件名) 此时它所操作的文件位于这个代码保存的位置
File file2 = new File("A:/", "word.txt"); //file(父文件夹,文件名)
File file3 = new File("A:/");
File file4 = new File(file3, "word.txt");//File(其他File对象,文件名)
//File既可以操作文件,还可以创建文件夹
//File类常用方法
File f1 = new File("test.txt");
System.out.println("文件是否存在: " + f1.exists()); //exists()方法用于判断此文件是否存在
if (f1.exists()) { //若文件存在则删除文件
boolean delete = f1.delete(); //delete()方法可以删除文件 返回是否成功删除此文件的布尔值
System.out.println("文件是否删除成功:" + delete);
}
try {
boolean create = f1.createNewFile(); //createNewFile()用于创建文件 若成功创建会返回true 若此文件存在则不会创建此文件
System.out.println("文件是否创建成功:" + create);
} catch (IOException e) { //若路径不存在或拒绝访问则抛出IO异常
e.printStackTrace();
}
File f2 = new File("test.txt");
System.out.println("f1和f2是同一个对象吗:" + (f1 == f2));
System.out.println("f1和f2操作的是同一个文件吗:" + f1.equals(f2));
File f3 = new File("src/com/IO/test.txt");
System.out.println("文件的名称:" + f3.getName());
System.out.println("文件的字节数:" + f3.length());
System.out.println("文件的最后修改时间:" + f3.lastModified());//注意此处为毫秒值
System.out.println("文件是否隐藏:" + f3.isHidden());
System.out.println("是否是文件夹:" + f3.isDirectory());
System.out.println("是否是文件:" + f3.isFile());
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("文件的最后修改时间:" + df.format(f3.lastModified()));
}
}
运行结果:
文件是否存在: false
文件是否创建成功:true
f1和f2是同一个对象吗:false
f1和f2操作的是同一个文件吗:true
文件的名称:test.txt
文件的字节数:0
文件的最后修改时间:0
文件是否隐藏:false
是否是文件夹:false
是否是文件:false
文件的最后修改时间:1970-01-01 08:00:00
字节流
字节流分为字节输出流和字节输入流,输出流指程序输出到文件中,输入流指程序从文件读取内容。
首先介绍字节输出流。字节输出流的类名为FileOutputStream。若要使用,首先要先实例化FileOutputStream类。该类的构造方法如下。
FileOutputStream(File file)
:使用文件file实例化字节输出流对象。其中file是一个已经实例化的File对象,实例化之后,这个字节输出流操作的文件对象就是File对象的操作文件了。FileOutputStream(File file, boolean append)
:若append参数为true代表以追加模式向文件中写入内容,即向文件中写入的内容会追加到文件末尾。
在实例化字节输出流后,调用这个对象的成员方法就可以对操作此文件了。下面是字节输出流的一些成员方法。
write(b)
:向文件中写入内容,其中写入的内容为b,即字节码,该方法没有返回值,若发生IO错误则会抛出IOException异常。close()
:关闭此字节流。若文件不需要再写入内容了或者不再使用了要及时关闭字节流以释放文件资源。
接下来介绍字节输入流。字节输入流的类名为FileInputStream。若要使用,需要先使用new FileInputStream(file)
实例化字节输入流,参数这里不再赘述。之后使用此对象的read(b)
即可读取文件中的内容,其中b为之前定义的缓冲区。该方法会返回一个整型值,代表成功读取的字节数。最后使用完字节输入流要及时关闭,以释放文件。
package IO.a;
import java.io.*;
public class a {
public static void main(String[] args){
//字节输入流和字节输出流
File f1 = new File("src/IO/a/a.txt"); //首先创建File对象 构造方法的参数就写文件的路径就可以了 注意和类的格式区分开
FileOutputStream fout = null;
try {
fout = new FileOutputStream(f1);
//创建字节输出流对象 参数是已经实例化的File对象 该字符输出流的操作对象就是这个File指定的文件
//在构造方法中,它还可以加上一个Boolean类型的参数,若此参数为true,则代表将数据写入文件末尾,若没有此参数或为false则为覆盖模式
byte[] b = "这是一个字符串".getBytes();//字节输入流只能根据字节码向文件写入信息,因此使用b存储字节码
fout.write(b); //先将字符串转换成字节码 然后用字节输出流对象的成员方法write()向参数中写入内容 该方法没有返回值
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fout != null) {
try {
fout.close(); //最后需要关闭文件
} catch (IOException e) {
e.printStackTrace();
}
}
}
File f2 = new File("src/IO/a/a.txt");
FileInputStream fin = null;
try {
fin = new FileInputStream(f2);
byte[] buffer = new byte[1024];//作为缓冲区
int length = fin.read(buffer); //read()一次性读取文件的内容并返回成功读取到的字节数 若没有成功读取到内容或者读取到末尾则返回-1
/*
* 因为缓冲区的长度为1024,如果文件的字节数不足1024,在字节流读取到末尾的时候,没有内容的部分将全部是空格
* 因为read()方法会返回一个int值,存储的是成功读取的字节数,因此使用length保存
* 在将字节编码转换为字符串的时候,使用String类的第三个构造方法即可去掉空格
* */
String content = new String(buffer, 0, length); //将字节码转换为字符串 直接调用String的构造方法即可
System.out.println("文件内容是:" + content);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fin != null) {
try {
fin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
文件内容是:这是一个字符串
字符流
由于字节流向文件中写入内容或从文件中是以字节为单位的,在处理中文字符或者外文字符时一个字符往往占据多个字节,若编码等问题处理不当会产生乱码。因此字符流便因此而生。字符流在处理文件时是以字符为单位的,这样无论是英文字符还是中文字符,无论一个字符占据多少个字节,都不会发生乱码。
因为字符流对应字节流,因此字符流也分为字符输入流和字符输出流。首先介绍字符输出流。
字符输出流的类名为FileWriter。使用字符输出流之前需要先实例化,语法是new FileWriter(File file)
。这个构造方法有一种重载方式,即FileWriter(File file, boolean append)
,其中append表示是否以追加方式写入文件。在实例化此对象之后,使用此对象的成员方法write(str)
即可写入字符串,其中str为字符串,因为字符流是以字符为单位操作文件的,因此不需要将str转换为字节数组。若发生IO错误则会抛出IOException异常。在最后,需要及时关闭字符输出流,使用close()
。
接下来介绍字符输入流。字符输入流的类名为FileReader。字符输入流在使用前也需要实例化,语法是new FileReader(file)
,其中file是一个File对象。在实例化此对象之后还需要准备一个字符缓冲区,如char[] buffer = new buffer[length]
。在实例化这两个类之后,调用FileReader对象的read(buffer)
方法即可读取字符,读取的字符将会存取到buffer中,需要特别注意这个缓冲区应是字符缓冲区。该方法会返回一个整型值,代表成功读取的字符数量。在最后使用完字节输入流需要即使关闭,调用对象的close()
方法即可关闭。
package IO.b;
import java.io.*;
public class b {
public static void main(String[] args) {
/*
* 由于英文字母仅占一个字节,在使用字节流时较方便,但每个汉字占2个字节,使用字节流会很麻烦甚至可能产生乱码
* 因此这里演示更为方便的字符流
* 字符流会读取每个字符,字符包括汉字和字母,因此不容易引发乱码等问题
* */
File f1 = new File("src/IO/b/b.txt");
FileWriter fw = null;
try {
fw = new FileWriter(f1);
String str = "明月几时有,把酒问青天";
fw.write(str); //字符输出流的write()方法不需要将字符串转换成字节数组 因为此时编译器会将此字符串转换成字符数组 当然也可以使用字符数组作为参数
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
File f2 = new File("src/IO/b/b.txt");
FileReader fr = null;
try {
fr = new FileReader(f2);
char[] buffer = new char[1024]; //字符流读取的是字符,所以要用char[]作为缓冲区
int length = fr.read(buffer);
String content = new String(buffer, 0, length);
System.out.println("文件的内容是:" + content);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
文件的内容是:明月几时有,把酒问青天
带缓冲区的字节流
带缓冲区的字节流相比字节流来说,它封装了缓冲区,效果是可以提高读取文件的效率。
首先介绍带缓冲区的字节输出流。带缓冲区的字节输出流对应的类名为BufferedOutputStream。在使用这个类之前,首先实例化File类,语法是new File(path)
然后将此File对象作为参数实例化FileOutputStream类,语法是new FileOutputStream(file)
,最后使用此FileOutputStream对象实例化BufferedOutputStream,语法是new BufferedOutputStream(fo)
,其中fo是之前实例化的FileOutputStream对象。之后使用这个BufferedOutputStream对象的write(b)
就可以将内容写入文件,其中b代表内容对应的字节数组。但是需要注意,使用write方法后,内容是先写入缓冲区,在文件关闭后或缓冲区满后才会写入文件。因此可以使用flush()
刷新缓冲区,刷新后缓冲区的内容就可以写入文件,或者也可以关闭文件,内容也会在文件被关闭后被写入文件中。在最后要按顺序及时关闭缓冲字节输出流,字节输出流。
接下来介绍带缓冲区的字节输入流。带缓冲区的字节输入流对应的类名为BufferedInputStream。使用此类前需要先实例化File类,然后使用此File对象实例化FileInputStream类,在使用FileInputStream实例化new BufferedInputStream(fi)
,其中fi为FileInputStream对象。在实例化之后,还需要准备一个缓冲区,假如命名为buf,那么调用前面实例化的FileInputStream对象的read(buf)
方法即可读取内容,读取后文件的内容就会存到buf中。该方法还会返回成功读取的字节数。在最后要及时按顺序关闭缓冲字节输入流、字节输入流。
需要注意的是,在使用带有缓冲区的字节流时不是不需要手动创建缓冲区了,是它在内存中进行包装而优化了性能。
package IO.c;
import java.io.*;
public class c {
public static void main(String[] args) {
//字节输入流和字符输出流都需要手动定义缓冲区 对性能有影响 使用缓冲字节流可以提高性能
//相当于给字符流包装起来,添加了一个缓冲区的作用
File f1 = new File("src/IO/c/c.txt");
FileOutputStream fout = null;
BufferedOutputStream bout = null;
try {
fout = new FileOutputStream(f1); //先创建没有缓冲区的字节输出流
bout = new BufferedOutputStream(fout); //再使用带有缓冲区的缓冲字节输出流将它包装起来
bout.write("人生自古谁无死".getBytes()); //注意区别不是不需要手动创建缓冲区了 是它在内存中进行包装而优化了性能
bout.flush(); //该方法可以在缓冲区还没有被写满的时候强制将缓冲区的内容写入到文件中
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bout != null) { //这里要注意流关闭的顺序,先创建的后关闭,否则会出现流未关闭的IOException异常
try {
bout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fout != null) {
try {
fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
File file2 = new File("src/IO/c/c.txt");
FileInputStream fin = null;
BufferedInputStream bin = null;
try {
fin = new FileInputStream(file2);
bin = new BufferedInputStream(fin); //这里也是先创建字节输入流再使用缓冲字节输入流包装 其他没有什么
byte[] buffer = new byte[1024];
int length = bin.read(buffer);
String content = new String(buffer, 0, length);
System.out.println("文件的内容是:" + content);
} catch (IOException e) {
e.printStackTrace();
}finally {
if (bin != null) { //这里要注意流关闭的顺序,先创建的后关闭,否则会出现流未关闭的IOException异常
try {
assert bout != null;
bout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fin != null) {
try {
assert fout != null;
fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行结果:
文件的内容是:人生自古谁无死
带缓冲区的字符流
缓冲字符流对应的是字符流。
先介绍如何使用缓冲字符输出流。缓冲字符流的类名为BufferedWriter,需要先实例化File类,语法是new File(path)
,其中path代表路径,然后实例化FileWriter,语法是new FileWriter(file)
,其中file是之前实例化的file对象,最后就可以实例化BufferedWriter类,语法是new BufferedWriter(fw)
,其中fw为之前实例化的FileWriter对象。它们之间是互相包装的关系。获取缓冲字符输出流的对象之后,就可以写入内容了。调用write(str)
方法来向文件中写入字符串,其中str代表要写入的字符串;调用newLine()
向文件中写入换行符。最后要关闭上面创建的三个对象,关闭顺序是先关闭缓冲字符输出流,然后关闭字符输出流。
接下来介绍如何使用缓冲字符输入流。缓冲字符输入流的类名为BufferedReader。需要先实例化File对象,然后使用此File对象实例化FileReader类,最后使用此FileReader对象实例化BufferedReader,之后就可以使用read(buf)
方法就可以读取文件并将文件内容存储到缓冲区buf中。该方法会返回一个整型值,代表成功读取的字符数。最后还是要按顺序关闭前面创建的两个对象。
package IO.d;
import java.io.*;
public class d {
public static void main(String[] args) throws IOException {
//本文件演示缓冲字符流 该流对应的是字符流 就像缓冲字符流对应字符流一样
File file1 = new File("src/IO/d/d.txt");
FileWriter fw;
BufferedWriter bw;
fw = new FileWriter(file1);
bw = new BufferedWriter(fw); //也是先创建字符流 然后使用缓冲字符流包装
bw.write("人生自古谁无死");
bw.newLine(); //写入一个换行符
bw.write("留取丹心照汗青");
bw.close();
fw.close();
File file2 = new File("src/IO/d/d.txt");
FileReader fr;
BufferedReader br;
fr = new FileReader(file2);
br = new BufferedReader(fr);
char[] content = new char[1024];
int length = br.read(content);
String str = new String(content, 0, length);
System.out.println("文件内容是:" + str);
br.close();
fr.close();
}
}
运行结果:
文件内容是:人生自古谁无死
留取丹心照汗青
数据流
数据流不按照规则、可以随意地向文件中读取内容或写入内容,但是读取的数据类型的顺序要和写入时的保持一致。这里不做讲解。
package IO.e;
import java.io.*;
public class e {
public static void main(String[] args) throws IOException {
//这里介绍数据输入流和数据输出流
//数据流也是包装其他流的流 通过数据流可以使用无关于系统的方式读取数据
File file = new File("src/IO/e/e.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);
dataOutputStream.writeUTF("这是一段文字");
dataOutputStream.writeInt(123);
dataOutputStream.writeBoolean(true);
//虽然这些方法实际写出的文件不可直接读取,但是可以通过下面的语句读取
FileInputStream fileInputStream = new FileInputStream(file);
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
System.out.println("dataInputStream.readUTF() = " + dataInputStream.readUTF());
System.out.println("dataInputStream.readInt() = " + dataInputStream.readInt());
System.out.println("dataInputStream.readBoolean() = " + dataInputStream.readBoolean());
fileOutputStream.close();
dataOutputStream.close();
fileInputStream.close();
dataInputStream.close();
}
}
运行结果:
dataInputStream.readUTF() = 这是一段文字
dataInputStream.readInt() = 123
dataInputStream.readBoolean() = true
网络编程
InetAddress类
InetAddress类是ip的包装类,InetAddress中提供了很多查找ip的方法。InetAddress类的构造方法不是公有的,不能通过new实例化。获取InetAddress对象的方法有以下几种。
InetAddress.getLocalHost()
:用于以本地ip初始化InetAddress类。InetAddress.getByName(Stringhost)
:以ip的名称初始化InetAddress类。
在获取InetAddress类后。调用这个对象的以下成员方法就可以获取和这个ip有关的信息。
getHostName()
:返回ip名称。getHostAddress()
:返回ip地址。getAllByName()
:返回所有IP地址。
package Inet;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class ClassIP {
//本文件演示如何使用ip的包装类InetAddress
public static void main(String[] args) throws UnknownHostException {
// InetAddress ip = new InetAddress(); //不能通过构造方法实例化
InetAddress ip1 = InetAddress.getLocalHost(); //此方法用于以本地ip初始化ip包装类
System.out.println(ip1);
InetAddress ip2 = InetAddress.getByName("www.baidu.com"); //以ip所在名称初始化
System.out.println("本机名称为:" + ip1.getHostName()); //getHostName()返回ip名称
System.out.println("百度ip地址为:" + ip2.getHostAddress()); //getHostAddress()返回ip地址
InetAddress[] ips = InetAddress.getAllByName("www.baidu.com"); //getAllByName()返回所有IP地址
for (InetAddress i : ips) {
System.out.println(i);
}
}
}
运行结果:
Computer/10.122.159.254
本机名称为:Computer
百度ip地址为:220.181.38.149
www.baidu.com/220.181.38.149
www.baidu.com/220.181.38.150
编写TCP程序
服务器
使用ServerSocket类实现TCP程序的服务器的步骤如下:
首先使用new ServerSocket(port)
实例化ServerSocket类,其中port表示端口,为int值。使用此方法实例化套接字对象后,使用accept()
方法等待用户端接入。此方法返回一个已经连接到客户端的Socket对象,之后对这个Socket对象进行操作就可以实现服务器和客户端之间的通信了。若要从客户端接收字符串,则只需要先获取服务器套接字的输入流,然后调用read(b)
方法读取即可,其中b为缓冲区,读取的字节流会存入此缓冲区,此方法会返回成功读取的字节数,为int值。若要向客户端发送字符串,则只需要先获取服务器的输出流,然后调用输出流的write(b)
方法即可,其中b为字节数组,对应要发送的字符串。
package Inet.TCP;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class server {
//本文件演示使用TCP技术实现网络通信 此文件是服务器端 需配合同目录下的socket文件使用 此文件先运行
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(1100); //直接使用端口作为参数实例化
System.out.println("服务器创建成功 等待客户端接入...");
Socket socket = server.accept(); //使用服务器套接字的accept()方法等待客户端接入 此为1对1
System.out.println("客户端连接成功");
//服务器从客户端接收字符串 使用字节数组传输数据 所以不注释了
InputStream in = socket.getInputStream(); //先获取服务器套接字的输入流
byte[] b1 = new byte[1024];
int length = in.read(b1);
String content = new String(b1, 0, length);
System.out.println("接收到来自客户端的字符串:" + content);
//服务器向客户端发送字符串
String string = "你好 我是客户端";
byte[] b2 = string.getBytes();
OutputStream out = socket.getOutputStream(); //获取服务器套接字的输出流
out.write(b2);
}
}
客户端
使用Socket类实现TCP程序的客户端的步骤如下:
首先使用new Socket(ip,port)
实例化套接字对象,其中ip是一个InetAddress对象,关于InetAddress类请见上文;port表示端口,是一个int值,这里的ip应为服务器的ip,端口要和服务器的端口保持一致。然后操作这个套接字对象就可以和服务器进行通信。若要向服务器发送字符串,只需要先获取套接字的输出流,然后调用输出流对象的write(b)
方法即可。若要从服务器接收字符串,只需要先获取套接字的输入流,然后调用输入流的read(b)
方法即可。
package Inet.TCP;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class socket {
public static void main(String[] args) throws IOException {
//本文件演示如何使用TCP技术实现网络通信 此文件为客户端 需配合同包下的server文件使用 此文件后运行
//客户端
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 1100); //通过ip地址和端口实例化套接字对象 也可使用字符串作为ip地址当参数
System.out.println("服务器连接成功");
//客户端向服务器发送字符串
String string = "你好 我是服务器";
byte[] b1 = string.getBytes();
OutputStream out = socket.getOutputStream(); //获取套接字对象的输出流
out.write(b1);
//服务器从客户端接收字符串
InputStream in = socket.getInputStream(); //获取套接字的输入流
byte[] b2 = new byte[1024];
int length = in.read(b2);
String content = new String(b2, 0, length);
System.out.println("接收到来自服务器的字符串:" + content);
}
}
运行结果:
服务器创建成功 等待客户端接入...
客户端连接成功
接收到来自客户端的字符串:你好 我是服务器
---
服务器连接成功
接收到来自服务器的字符串:你好 我是客户端
编写UDP程序——使用数据报包套接字
数据报包套接字是DatagramSocket类,数据报包套接字让不同的终端通过数据报包进行UDP通信。使用此类实现通信程序时,需要实例化数据报包套接字,实例化数据报包对象,准备好缓冲区,然后使用数据报包套接字的发送方法和接收方法就可以进行通信了。
发送端(服务器)
使用DatagramSocket类实现发送端的步骤如下:
准备 首先使用new DatagramSocket()
实例化数据报包套接字对象;将要发送的内容转换为字节数组;使用new DatagramPacket(buf, 0, buf.length, ip, port)
实例化数据报包,其中buf为要发送的内容转换后的字节数组;0代表发送的起始位置,可以是其他值;第三个参数代表字节数组的长度,可以是其他值;ip代表数据报包发送的目标ip;port代表端口,并准备发送此数据报包。
发送 使用前面数据报包套接字的send(p)
发送数据报包,其中p为之前创建的数据报包对象。
接收端(客户端)
使用DatagramSocket类编写接收端的步骤如下:
准备 首先使用new DatagramSocket(port,ip)
实例化数据报包套接字对象,其中ip表示发送端的ip地址,port表示端口。创建一个字节数组作为缓冲区,然后使用new DatagramPacket(buf, 0, length, ip, port)
实例化数据报包对象,其中buf为前面创建的字节数组;0代表起始位置,可换为其他参数;length代表缓冲区长度;ip代表发送端ip;port代表发送端端口,后两个参数可省略。
接收 使用上面创建的数据报包套接字对象的receive(p)
方法接收数据报包,其中p为上面创建好的数据报包对象,该方法执行后p中将存储有关发送端发送数据的信息。使用p.getData()
获取数据(字节数组,若要转换成字符串可以使用new String(p.getData(), 0, p.getLength())
,使用p.getLength()
获取数据的长度。
package Inet.UDP;
import java.io.IOException;
import java.net.*;
public class receive {
//此文件演示如何使用UDP技术实现网络通信 此文件使用单播数据套接字类 需配合同包下的server文件使用 此文件后运行
InetAddress ip;
int port = 1469;
DatagramSocket datagramSocket;
DatagramPacket datagramPacket;
receive() {
try {
ip = InetAddress.getByName("127.0.0.1");
datagramSocket = new DatagramSocket(port,ip);
byte[] buf = new byte[1024];
datagramPacket = new DatagramPacket(buf, 1024);
String content;
System.out.println("接收广播");
while (true) {
datagramSocket.receive(datagramPacket);
content = new String(datagramPacket.getData(), 0, datagramPacket.getLength());
System.out.println(content);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
} catch (UnknownHostException e) {
throw new RuntimeException(e);
} catch (SocketException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
new receive();
}
}
package Inet.UDP;
import java.io.IOException;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.Date;
public class send {
//本文件演示如何使用UDP实现网络通信 模拟广播播放天气预报的场景 本文件使用单播数据套接字类 需配合同包下的socket文件使用 此文件先运行
String weather;
int port = 1469;
InetAddress ip;
DatagramSocket datagramSocket;
DatagramPacket datagramPacket;
send() {
try {
ip = InetAddress.getByName("127.0.0.1");
datagramSocket = new DatagramSocket();
byte[] buf;
System.out.println("开始发送广播");
while (true) {
weather = new SimpleDateFormat("HH:mm:ss").format(new Date()) + ": 天气晴";
buf = weather.getBytes();
datagramPacket = new DatagramPacket(buf, 0, buf.length, ip, port);
datagramSocket.send(datagramPacket);
System.out.println(weather);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
new send();
}
}
编写UDP程序——使用多播数据套接字
使用多播数据套接字编写UDP程序的步骤大体如下:实例化多播数据套接字对象,加入广播组,然后在广播组中发送和接收数据即可。
使用MulticastSocket类编写UDP发送端的步骤如下:
首先使用new MulticastSocket(port)
实例化多播数据套接字,其中port指端口,然后调用这个套接字对象的joinGroup(ip)加入广播ip。准备缓冲区,如命名为buff,长度由发送内容的大小决定。然后使用new DatagramPacket(buff, 0, buff.length, ip, port)
实例化数据报包对象,其中buff为上面定义的缓冲区名称;0为缓冲区的起始位置;buff.length为缓冲区的长度;ip指目标ip;port为端口。最后调用send(p)
方法发送数据包,其中p为已经打包好的数据包,使用receive(p)
实现接收数据包,其中p为准备好的数据包,成功接收后p会存放数据。
一些关于TCP程序设计和UDP程序设计的小提示:在IP选择上,若使用ServerSocket类和Socket类编写TCP程序时,若交流双方都是本地计算机,那么IP为127.0.0.1;若使用多播数据报包套接字,那么IP要在224.0.0.0~225.225.225.255之间。在端口的选择上,端口不能选择已被其他应用程序占用的端口,一般来说端口1~1024都已被系统进程占用,除此之外,电脑的应用程序也会占用一些端口,如MySQL占用3306端口。
Java连接数据库
Java连接数据库需要三个类:Connection连接、Statement语句、ResultSet结果集。其中Connection类提供了和很多类型的数据库的连接接口;Statement类用于向数据库发送SQL语句,ResultSet类用于接收数据库返回的结果集,这三个缺一不可。下面用具体的例子演示如何使用JDBC技术连接数据库。
DriverManager类是一个驱动程序类,这里面提供了连接很多数据库的驱动。在获取连接对象时,使用DriverManager.getConnection(url,user,password)
语句,其中url代表连接数据库的url,user代表用户名,password代表密码。将这个方法的返回值赋值给一个Connection变量后,调用这个对象的createStatement()
方法就可以获取Statement对象了。将此对象赋值给一个Statement变量之后,就可以使用这个Statement对象的executeQuery(s)
方法和executeUpdate(s)
执行SQL语句了,其中executeQuery(s)
会执行不改变数据库或数据表结构的语句,如查询语句,该方法会返回一个结果集对象,结果集后面会提到;executeUpdate(s)
会执行改变数据库或数据表结构的语句,如插入语句,该方法会返回一个int类型的值,代表影响的数据行个数。在最后要按照使用顺序使用close()
方法关闭结果集对象,语句对象和连接对象。
结果集介绍:结果集中存放了数据库返回的结果,结果集的成员方法next()
是将指针移到下一个数据行,并返回true,若没有数据则返回false。成员方法getXXX(columnIndex)
则代表返回该行数据的第columnIndex个元素的值,如第一个字段是varchar类型,就使用getString(1)
获取数据,如第二个字段是int类型,就是要getInt(2)
获取数据。
package SQL;
import java.sql.*;
public class DemoSQL {
public static void main(String[] args) throws SQLException {
//读取数据库中的信息
//在使用前,请在MySQL中以root用户创建数据库stu,在其下创建tb_stu数据表,并导入以下信息
/* id name birthday
* 1 'a' 2020-01-01
* 2 'b' 2020-01-02
* 3 'c' 2020-01-03
* */
Connection con; //Connection接口用于连接数据库
Statement stmt; //Statement接口用于给数据库发送语句
ResultSet res; //ResultSet接口用于接收数据库返回的结果
con = DriverManager.getConnection("jdbc:mysql://localhost:3306", "root", "254456"); //第一步 先使用Connection接口连接数据库
stmt = con.createStatement(); // 第二步 使用连接后的Connection对象创建语句对象
res = stmt.executeQuery("select * from stu.tb_stu"); //对数据库进行查询的操作就用Query语句
while (res.next()) {
System.out.println("id = " + res.getInt(1) + " name = " + res.getString(2) + " birthday = " + res.getString(3));
res.close();
stmt.close();
con.close();
}
}
}
package SQL;
import java.sql.*;
public class statement {
public static void main(String[] args) throws SQLException {
//statement用于代替数据库执行操作 首先要连接数据库 然后使用连接的对象创建statement对象 然后使用executeUpdate()执行更新数据的操作 使用executeQuery()执行查看的操作
Connection con;
Statement stmt;
//在运行此部分代码前 请确保idea已经连接到数据库stu 否则代码解析不能正常运行
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/stu", "root", "254456");
stmt = con.createStatement();
//1.添加数据
//注:可以同步打开数据库查看结果 将2和3注释掉再运行此部分
// int result1 = stmt.executeUpdate("insert into tb_stu (id, name, birthday) VALUES (4,'d','2020-01-04)')");
// System.out.println("有"+result1+"行被修改");
//2.修改数据
//注:可以同步打开数据库查看结果 将1和3注释掉再运行此部分
// int result2 = stmt.executeUpdate("update tb_stu set birthday = '2020-01-05' where birthday = '2020-01-04'");
// System.out.println("有"+result2+"行被修改");
//3.删除数据
//注:可以同步打开数据库查看结果 将1和2注释掉再运行此部分
// int result3 = stmt.executeUpdate("delete from tb_stu where id = 4");
// System.out.println("有"+result3+"行被修改");
stmt.close();
con.close();
}
}
package SQL;
import java.sql.*;
public class preparedStatement {
public static void main(String[] args) throws SQLException {
Connection con;
PreparedStatement prestmt;
ResultSet res;
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/stu", "root", "254456");
//进行预处理时 可以用?代表通配符 在程序运行之前先用set函数指定通配符的内容 可以提高程序运行效率
prestmt = con.prepareStatement("select * from tb_stu where id = ?");
prestmt.setInt(1, 1);
//等同于执行select * from tb_stu where id = 1;
res = prestmt.executeQuery();
while (res.next()) {
System.out.println("id = " + res.getInt(1) + " name = " + res.getString(2) + " birthday = " + res.getString(3));
} //结果集可以调用getXXX()方法获取详细的值 参数是列名或列数
prestmt = con.prepareStatement("select * from tb_stu where name = ?");
prestmt.setString(1, "c");
res = prestmt.executeQuery();
while (res.next()) {
System.out.println("id = " + res.getInt(1) + " name = " + res.getString(2) + " birthday = " + res.getString(3));
res.close();
prestmt.close();
con.close();
}
}
}
窗体类
Swing是Javax提供的一套GUI框架。使用Swing可以开发出窗体程序,使数据可视化。
第一个Swing程序
JFrame是所有窗体类的父类,创建一个窗体有两种方式。第一种方式是在主方法里实例化JFrame对象,然后设置此对象即可;第二种方式就是自定义一个窗体类继承自JFrame类,然后在构造方法里设置属性,再在主方法里创建此对象即可。
JFrame类的构造方法即意义如下:
public JFrame()
:创建一个无标题的窗体。public JFrame(String title)
:创建一个标题为title的窗体。
下方的示例代码是使用第二种方式创建窗体的,在日常开发中也是这样使用窗体。
package swing.Dialogs;
import javax.swing.*;
import java.awt.*;
public class DemoJFrame extends JFrame {
private DemoJFrame() {
super("这是一个窗体"); //构造方法 参数是标题
setVisible(true); //设置窗体可见
Container container = getContentPane(); //创建窗体容器,可向容器中添加组件以在窗体中显示组件
JLabel jLabel = new JLabel("这是一个JLabel文本"); //创建文本框
jLabel.setHorizontalAlignment(SwingConstants.CENTER); //设置文本框的位置
container.add(jLabel); //向容器里添加这个文本框
container.setBackground(Color.ORANGE); //设置背景颜色
setSize(400, 200); //设置大小
setLocation(300, 200); //设置位置
//还有可以一起设置位置和大小的方法 即jFrame.setBounds(int x,int y,int width,int height)
//下面演示如何设置窗体的最小大小
Dimension dimension = new Dimension(400, 200); //先创建该对象 参数指定最小长和高
setMinimumSize(dimension); //然后调用此方法即可
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); //设置在程序关闭时结束进程运行
}
public static void main(String[] args) {
new DemoJFrame(); // 创建新窗体
}
}
运行结果如下:
一般窗体——JFrame类
JFrame类在上方已经介绍过是所有窗体的父类,它还有以下常用方法,这些方法都可以在继承自它的子类中使用,因此下方的代码将不再说明这些方法的意义。
public void setDefaultCloseOperation(int operation)
:设置窗体在点击关闭按钮后线程的执行方式,有三个可选值:DO_NOTHING_ON_CLOSE
(在WindowConstants
中定义):不执行任何操作;要求程序在已注册的WindowListener
对象的windowClosing
方法中处理该操作。HIDE_ON_CLOSE
(在WindowConstants
中定义):调用任意已注册的WindowListener
对象后自动隐藏该窗体。DISPOSE_ON_CLOSE
(在WindowConstants
中定义):调用任意已注册WindowListener
的对象后自动隐藏并释放该窗体。EXIT_ON_CLOSE
(在JFrame
中定义):使用System
exit
方法退出应用程序。仅在应用程序中使用。
setTitle(String title)
:设置窗体标题。public void setLayout(LayoutManager manager)
:设置窗体的布局方式,在后面会介绍。public Container getContentPane()
:获取窗体容器。public void setSize(int width, int height)
:设置窗体的大小。public void setLocation(int x, int y)
:设置窗体的位置。public void setBounds(int x, int y, int width, int height)
:设置窗体的大小和位置。public void setVisible(boolean b)
:设置窗体是否可见。
对话框——JDialog类
对话框相对于窗体来说,最大的区别就是右上角没有最大化按钮。对话框一般用于确认信息、输入信息、提示信息等作用。Java中,使用对话框需要用到JDialog类。JDialog类的构造方法如下:
public JDialog()
:创建一个没有父窗体、不阻塞父窗体的对话框。public JDialog(Frame owner)
:创建一个父窗体为owner,不阻塞父窗体的对话框。public JDialog(Frame owner, boolean modal)
:创建一个父窗体为owner的对话框。若modal参数为True,则对话框阻塞父窗体。public JDialog(Frame owner, String title
):创建一个父窗体为owner,标题为title,不阻塞父窗体的对话框。public JDialog(Frame owner, String title, boolean modal)
:创建一个父窗体为owner,标题为title的对话框。若modal参数为True,则对话框阻塞父窗体。
JDialog的常用方法和JFrame很类似:
setTitle(String title)
:设置对话框标题。public void setLayout(LayoutManager manager)
:设置对话框的布局方式,在后面会介绍。public Container getContentPane()
:获取对话框容器。public void setSize(int width, int height)
:设置对话框的大小。public void setLocation(int x, int y)
:设置对话框的位置。public void setBounds(int x, int y, int width, int height)
:设置对话框的大小和位置。public void setVisible(boolean b)
:设置对话框是否可见。
示例代码:
在下面的代码中,静态内部类MyJFrame是父窗体,在对话框中,将MyJFrame对象作为参数传入。
package swing.Dialogs;
import javax.swing.*;
import java.awt.*;
//该文件演示如何使用对话框
public class DemoJDialog extends JDialog { //继承对话框
private DemoJDialog(JFrame frame) {
/*
* 第一个参数是父对话框对象
* 第二个参数是对话框标题
* 第三个参数是是否阻塞 true为阻塞
* */
super(frame, "这是一个对话框", true);
setBounds(500, 400, 200, 100); //设置对话框的大小和位置
Container container = getContentPane(); //对话框的容器
container.add(new JLabel("这是一个对话框")); //添加组件
setVisible(true); //最后设置可见
}
public static void main(String[] args) {
new MyJFrame();
}
private static class MyJFrame extends JFrame {
private MyJFrame() {
super("这是一个窗体"); //父窗体对象
setBounds(550, 450, 300, 200); //设置位置和大小
setVisible(true); //设置可见
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); //设置关闭方式
Container container = getContentPane(); //容器
container.setLayout(null); //容器使用绝对布局
JButton jButton = new JButton("弹出对话框"); //按钮
jButton.setBounds(50, 50, 100, 30); //设置按钮位置和大小
jButton.addActionListener(e -> new DemoJDialog(this)); //为按钮添加监听
container.add(jButton);
}
}
}
运行结果如下。当点击“弹出对话框”按钮时,会弹出小型对话框。
小型对话框——JOptionalPane类
JOptionalPane类提供了一些静态方法用于显示一些简易的小型对话框:
- 选择对话框:
public static int showConfirmDialog(Component parentComponent,Object message, String title, int optionType)
:
- parentComponent代表父窗体,和JDialog类似;
- message代表显示在对话框内部的消息;
- title代表对话框标题;
- optionType代表选择类型,有以下几个选项:
DEFAULT_OPTION
:只显示确定按钮。YES_NO_OPTION
:显示确定和取消两个按钮。YES_NO_CANCEL_OPTION
:显示确定、取消和返回三个按钮。OK_CANCEL_OPTION
:显示确定和返回两个按钮。
该方法会返回一个整型值,代表选择的选项索引。如optionType选择YES_NO_OPTION,则在点击确定按钮后返回0,点击取消按钮后返回1,点击右上角的关闭按钮时返回-1。
- 信息对话框:
public static void showMessageDialog(Component parentComponent,Object message, String title, int messageType)
:
参数和返回值与第一个对话框的参数意义类似。
- 输入对话框:
public static String showInputDialog(Component parentComponent,Object message, String title, int messageType)
:参数与第一个对话框类似,但是此对话框多了一个输入框,返回值变成了String类型,即对话框的输入内容。
- 自定义对话框:
public static int showOptionDialog(Component parentComponent,Object message, String title, int optionType, int messageType, Icon icon, Object[] options, Object initialValue)
:
- messageType代表信息风格,可选值有ERROR_MESSAGE、INFORMATION_MESSAGE、WARNING_MESSAGE、QUESTION_MESSAGE或PLAIN_MESSAGE;
- icon代表图标;
- options代表选项,为对象数组;
- initialValue:默认选中的对象。
返回值与选择对话框类似,返回的是对象数组的索引。
下面代码用于显示四种小型对话框:
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JOptionPane.showConfirmDialog(null, "Confirm", "Title", JOptionPane.OK_CANCEL_OPTION);
JOptionPane.showMessageDialog(null, "Message", "Title", JOptionPane.INFORMATION_MESSAGE);
JOptionPane.showInputDialog(null, "Confirm", "Title", JOptionPane.PLAIN_MESSAGE);
JOptionPane.showOptionDialog(null, "Option", "Title", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{"选项一", "选项二"}, null);
}
}
Swing布局
布局指容器中各个组件的排列方式,Swing中常用的布局有绝对布局、流布局、边界布局和网格布局。
绝对布局——null
若容器应用了绝对布局,则容器内每个组件都会按照它本身设定的位置和大小排列。使用方式是先调用窗体容器对象的setLayout(null)方法将窗体设为绝对布局,然后对每个组件调用setBounds()设定大小和位置。
package swing.Layouts;
import javax.swing.*;
import java.awt.*;
public class Null extends JFrame {
//容器布局之一 绝对布局
private Null() {
super("标题");
Container container = getContentPane();
container.setLayout(null); //设为绝对布局
JButton jButton1 = new JButton("按钮1");
jButton1.setBounds(20, 20, 100, 50);
JButton jButton2 = new JButton("按钮2");
jButton2.setBounds(100, 200, 150, 50);
container.add(jButton1);
container.add(jButton2);
setBounds(100, 100, 500, 400);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new Null();
}
}
运行结果:
流布局——FlowLayout
流布局比较常用,效果有些像CSS里面的Flex布局。应用此布局后,组件会从左到右摆放,若该行溢出则将组件移动在下一行摆放,以此类推。使用此布局方式要使用窗体容器对象的setLayout(flowLayout)方法,其中flowLayout是FlowLayout类的一个对象。
FlowLayout类的构造方法如下:
public FlowLayout()
。public FlowLayout(int align)
。public FlowLayout(int align, int hgap, int vgap)
。
参数align代表当某行的元素数量不足最大元素数量时,元素的排列方向,可选值有FlowLayout.LEFT,代表每一行的组件指定为左对齐;还有FlowLayout.CENTER和FlowLayout.RIGHT。
参数hgap指定水平间隔,vgap指定垂直间隔,单位都是像素。
示例代码:
package swing.Layouts;
import javax.swing.*;
import java.awt.*;
public class DemoFlowLayout extends JFrame {
private DemoFlowLayout() {
super("标题");
Container container = getContentPane();
//参数1为对齐方式 参数2为水平间距 参数3为垂直间距
container.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10)); //设为绝对布局
for (int i = 1; i < 20; i++) {
container.add(new JButton("按钮" + i));
}
setBounds(100, 100, 500, 400);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
//容器布局之一 流布局
public static void main(String[] args) {
new DemoFlowLayout();
}
}
运行结果如下。当改变窗口的大小时元素的排列方式也会改变。
边界布局——BorderLayout
边界布局类似于Web页面的圣杯式布局,它将整个页面分为东南西北中五个区域。
若窗体应用了边界布局,则在每次添加元素时需要指定元素所在的位置。
将窗体设置为边界布局的方式也是调用setLayout()方法,需要传入一个BorderLayout对象。
BorderLayout类的成员变量如下:
public static final String NORTH;
public static final String SOUTH;
public static final String EAST;
public static final String WEST;
public static final String CENTER。
它们的字面值就代表了它们的意义。在添加组件时,指定位置使用的就是它们。
public void add(Component comp, Object constraint)
方法用于向容器中添加组件,并且可以指定组件的位置。
示例代码:
package swing.Layouts;
import javax.swing.*;
import java.awt.*;
public class DemoBorderLayout extends JFrame{
//容器布局之一 边界布局
//该布局分为东南西北中五个区域
private DemoBorderLayout(){
super("标题");
Container container = getContentPane();
container.setLayout(new BorderLayout());
JButton jButton1 = new JButton("东");
JButton jButton2 = new JButton("南");
JButton jButton3 = new JButton("西");
JButton jButton4 = new JButton("北");
JButton jButton5 = new JButton("中");
container.add(jButton1,BorderLayout.EAST);
container.add(jButton2,BorderLayout.SOUTH);
container.add(jButton3,BorderLayout.WEST);
container.add(jButton4,BorderLayout.NORTH);
container.add(jButton5,BorderLayout.CENTER);
//重复添加时会覆盖之前添加的组件
setBounds(100,100,300,300);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoBorderLayout();
}
}
网格布局——GridLayout
网格布局很好理解,就是按照顺序将组件放到预先设定好的表格中。在设置网格布局时,需要的布局对象为GridLayout对象。该类的构造方法如下:
public GridLayout(int rows, int columns)
。public GridLayout(int rows, int cols, int hgap, int vgap)
。
rows指定行数,columns指定列数,hgap指定水平间隔,vgap指定垂直间隔。
示例代码:
package swing.Layouts;
import javax.swing.*;
import java.awt.*;
public class DemoGridLayout extends JFrame {
//容器布局之一 网格布局
private DemoGridLayout() {
super("标题");
Container container = getContentPane();
//参数1为行数 参数2为列数 参数3为水平间距 参数4为垂直间距
container.setLayout(new GridLayout(5, 5, 5, 5)); //设为网格布局
for (int i = 1; i <= 20; i++) {
container.add(new JButton("按钮" + i));
}
setBounds(100, 100, 500, 400);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoGridLayout();
}
}
运行结果:
Swing组件
面板
Swing中,窗体有一个容器,组件要添加到容器中才会显示,每一个面板也是一个容器。
普通面板——JPanel类
JPanel是最常用的面板。它必须在窗体中被使用。它自己就是一个容器,因此它可以包含组件、设置布局方式、设置边框等。它的使用方法和窗体的容器类似。
示例代码:
package swing.Panel;
import javax.swing.*;
import java.awt.*;
public class DemoJPanel extends JFrame {
private DemoJPanel(){
super("标题");
Container container = getContentPane();
container.setLayout(new GridLayout(2, 2, 5, 5));
//在JPanel的构造方法中 参数可以带有布局风格 因为面板也算是一种容器
JPanel jPanel1 = new JPanel();
jPanel1.setLayout(new BorderLayout());
//可以像上面一样后使用setLayout()方法
JPanel jPanel2 = new JPanel(new GridLayout(2, 1));
JPanel jPanel3 = new JPanel(new GridLayout(1, 2));
JPanel jPanel4 = new JPanel(new FlowLayout(FlowLayout.LEFT));
jPanel1.setBorder(BorderFactory.createTitledBorder("面板1"));
jPanel2.setBorder(BorderFactory.createTitledBorder("面板2"));
jPanel3.setBorder(BorderFactory.createTitledBorder("面板3"));
jPanel4.setBorder(BorderFactory.createTitledBorder("面板4"));
jPanel1.add(new JButton("按钮1"), BorderLayout.WEST);
jPanel1.add(new JButton("按钮1"), BorderLayout.EAST);
jPanel1.add(new JButton("按钮1"), BorderLayout.NORTH);
jPanel1.add(new JButton("按钮1"), BorderLayout.SOUTH);
jPanel1.add(new JButton("按钮1"), BorderLayout.CENTER);
jPanel2.add(new JButton("按钮2"));
jPanel2.add(new JButton("按钮2"));
jPanel3.add(new JButton("按钮3"));
jPanel3.add(new JButton("按钮3"));
for (int i = 0; i < 20; i++) {
jPanel4.add(new JButton("按钮4"));
}
container.add(jPanel1);
container.add(jPanel2);
container.add(jPanel3);
container.add(jPanel4);
setBounds(100, 100, 500, 400);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoJPanel();
}
}
运行结果:
滚动面板——JScrollPane类
滚动面板用于在较小的容器中显示较大的组件,一般用于给文本域、表格加滚动条。
它的使用方法是在实例化时将组件作为参数传入构造方法里,然后向容器中添加滚动面板。
示例代码:
package swing.Panel;
import javax.swing.*;
import java.awt.*;
public class DemoJScrollPanel extends JFrame {
//本文件演示滚动窗体
private DemoJScrollPanel() {
super("标题");
Container container = getContentPane();
JTextArea jTextArea = new JTextArea();
jTextArea.setFont(new Font(null, Font.PLAIN, 20));
JScrollPane jScrollPane = new JScrollPane(jTextArea); //构造方法里的参数写要创建滚动面板的组件 不要使用add()方法
container.add(jScrollPane);
setBounds(100, 100, 300, 200);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoJScrollPanel();
}
}
运行结果如下。当文本域内容过多时,它会在指定方向添加滚动条。
标签——JLabel类
JLabel类用于显示一个标签,此标签可以显示图片、文字,不可被选中和编辑。
JLabel类的常用构造方法如下:
public JLabel()
:创建一个无图标、无文字的标签。public JLabel(String text)
:创建一个文字为text的标签。
JLabel的常用方法如下:
public void setText(String text)
:设置标签的文本。public void setSize(int width, int height)
:设置标签的大小。public void setLocation(int x, int y)
:设置标签的位置。public void setBounds(int x, int y, int width, int height)
:设置标签的大小和位置。public void setVisible(boolean b)
:设置标签是否可见。public void setFont(Font font)
:设置标签的字体。public void setForeground(Color fg)
:设置字体颜色。public void setBackground(Color bg)
:设置背景颜色。
示例代码:
package swing.Label;
import javax.swing.*;
import java.awt.*;
import java.net.URL;
import java.util.Objects;
public class DemoJLabel extends JFrame {
//标签 JLabel
private DemoJLabel() {
super("标题");
Container container = getContentPane();
container.setLayout(new FlowLayout(FlowLayout.CENTER)); //若容器设为绝对布局 则组件要同时设置大小和位置
//图标Icon类 构造方法内可选参数分别是
URL url = DemoJLabel.class.getResource("Java.jpeg"); //URL类 存储文件的路径
System.out.println("该图片的路径为:" + url);
Icon icon = new ImageIcon(Objects.requireNonNull(url)); //图标对象 存储图标
//也可使用字符串 内是它的地址
JLabel jLabel1 = new JLabel("这是一个JLabel标签", SwingConstants.CENTER);
//还可以设置标签的颜色 字体
Font font = new Font("宋体", Font.BOLD, 15);
jLabel1.setFont(font);
jLabel1.setForeground(Color.GREEN);
JLabel jLabel2 = new JLabel(icon);
container.add(jLabel1);
container.add(jLabel2);
setBounds(100, 100, 500, 350);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoJLabel();
}
}
运行结果如下:
按钮组件
普通按钮——JButton类
它类似于HTML中的按钮,点击可以触发事件。
JButton类的构造方法如下:
public JButton()
:创建一个无文字的按钮。public JButton(String text)
:创建一个带有文字信息的按钮。public JButton(Icon icon)
:创建一个带图标的按钮。public JButton(String text, Icon icon)
:创建一个带文字和图标的按钮。
JButton类的成员方法如下:
setIcon(Icon defaultIcon)
:设置按钮图标。setToolTipText(String text)
:为按钮设置提示文字。setBorderPainted(boolean b)
:设置是否绘制按钮边框,b若为true则绘制。setEnable(boolean b)
:设置按钮是否可用。setFocusPainted(boolean b)
:设置是否显示按钮焦点框。
示例代码:
package swing.Button;
import javax.swing.*;
import java.awt.*;
public class DemoJButton extends JFrame {
//本文件演示如何使用按钮 基本操作包括给按钮添加边框 设置颜色 设置是否可用 设置悬停提示 以及点击后发生事件
private DemoJButton() {
super("标题");
setBounds(100, 100, 500, 300);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
Container container = getContentPane();
container.setLayout(new GridLayout(2, 4, 10, 10));
JButton[] jButtons = new JButton[8];
for (int i = 0; i < jButtons.length; i++) {
jButtons[i] = new JButton();
container.add(jButtons[i]);
}
//按钮1设置为原始按钮 方便对比
jButtons[0].setText("原始按钮");
//按钮2设置为不可用
jButtons[1].setText("不可用");
jButtons[1].setEnabled(false); //setEnabled()方法设置按钮是否可用 实际上所有组件都可以使用这个方法
//按钮3设置背景颜色为红色
jButtons[2].setText("红色按钮");
jButtons[2].setBackground(Color.RED); //setBackground()方法设置背景颜色
//按钮4设置为无边框
jButtons[3].setText("无边框");
jButtons[3].setBorderPainted(false); //setBorderPainted()方法设置按钮是否有边框
//按钮5设置为绿色边框
jButtons[4].setText("绿色边框");
jButtons[4].setBorder(BorderFactory.createLineBorder(Color.GREEN)); //setBorder()方法设置按钮的边框
//按钮6设置为图片按钮
Icon icon = new ImageIcon("src/swing/Button/Java.jpeg"); //可以使用向上转型创建 也可以使用ImageIcon的getIcon()方法
jButtons[5].setText("图片按钮");
jButtons[5].setIcon(icon); //setIcon()设置图片的图标 参数是一个Icon对象
//按钮7设为悬停显示提示
jButtons[6].setText("悬停显示提示");
jButtons[6].setToolTipText("提示"); //setToolTipText()方法设置悬停提示
//按钮8设为可点击
jButtons[7].setText("可点击按钮");
jButtons[7].addActionListener(e -> JOptionPane.showConfirmDialog(this, "这是一个对话框"));
setBounds(100, 100, 500, 300);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoJButton();
}
}
运行结果:
单选按钮——JRadioButton类
单选按钮使用JRadioButton表示,它的构造方法如下所示:
public JRadioButton()
:创建一个无图标无文本的单选按钮。public JRadioButton(Icon icon)
:创建一个带有图标的单选按钮。public JRadioButton(String text)
:创建一个带有文本的单选按钮。public JRadioButton(String text, Icon icon)
:创建一个带有文本、带有图标的单选按钮。public JRadioButton(String text, Icon icon, boolean selected)
:创建一个带有文本、带有图标、设置选中状态的按钮。
为了实现单选按钮的排斥功能,一般需要将这些按钮放到一个按钮组中。按钮组使用ButtonGroup表示,在实例化后,将需要相互排斥的按钮使用add()方法加到按钮组中即可。
示例1:
package swing.Button;
import javax.swing.*;
import java.awt.*;
public class DemoJRadioButton extends JFrame {
private DemoJRadioButton() {
//本文件演示怎么使用单选按钮
//运行此代码块时请注释掉下面的代码块 这个代码块仅演示单选按钮如何使用
super("标题");
setBounds(200, 200, 300, 200);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
Container container = getContentPane();
container.setLayout(new GridLayout(2, 1));
JRadioButton jRadioButton = new JRadioButton("单选按钮");
container.add(jRadioButton);
JButton jButton = new JButton("检测");
JPanel jPanel = new JPanel();
jPanel.add(jButton);
jButton.setSize(40,21);
container.add(jPanel);
jButton.addActionListener(e -> {
System.out.println("\"单选按钮\"有没有被选中?" + jRadioButton.isSelected()); //isSelected()方法可以返回该按钮是否被选中的布尔值
});
}
public static void main(String[] args) {
new DemoJRadioButton();
}
}
运行结果:
点击检测按钮后会输出单选按钮的选中状态。
示例2:
package swing.Button;
import javax.swing.*;
import java.awt.*;
public class DemoJRadioButton extends JFrame {
private DemoJRadioButton() {
//下面演示怎么设置一个按钮若选中就取消另一个按钮的选择 实现方法是将两个按钮添加到一个按钮组中
super("标题");
Container container = getContentPane();
container.setLayout(new GridLayout(1, 2));
JRadioButton jRadioButton1 = new JRadioButton("选项1");
JRadioButton jRadioButton2 = new JRadioButton("选项2");
ButtonGroup buttonGroup = new ButtonGroup(); //定义按钮组
buttonGroup.add(jRadioButton1);
buttonGroup.add(jRadioButton2); //添加两个按钮 这样两个按钮就进入这个按钮组了 就会成为单选按钮
jRadioButton1.setSelected(true); //这个方法可以设置该按钮是否默认被选中
container.add(jRadioButton1);
container.add(jRadioButton2);
setBounds(200, 200, 300, 200);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
//若想清除按钮的选择状态 可以调用按钮组对象的clearSelection()方法
}
public static void main(String[] args) {
new DemoJRadioButton();
}
}
运行结果:
这两个按钮相互排斥,在选中选项2后选项1会取消勾选。
复选框——JCheckBox类
复选框使用JCheckBox表示,它有两个选中方式:选中和未选中,有的复选框会有半选中方式。JCheckBox类的构造方法如下:
public JCheckBox()
:创建一个没有文本没有图标的复选框。public JCheckBox(String text)
:创建一个文本为text的复选框。public JCheckBox(String text, Icon icon)
:创建一个有文本有图标的复选框。
示例代码:
package swing.Button;
import javax.swing.*;
import java.awt.*;
public class DemoJCheckBox extends JFrame{
//该文件演示如何使用复选框
private DemoJCheckBox(){
super("标题");
setBounds(100, 100, 500, 300);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
JCheckBox jCheckBox1 = new JCheckBox("按钮1");
jCheckBox1.setSelected(true); //该方法会设置复选框的默认选中状态
JCheckBox jCheckBox2 = new JCheckBox("按钮2");
JCheckBox jCheckBox3 = new JCheckBox("按钮3");
Container container = getContentPane();
container.setLayout(new FlowLayout());
container.add(jCheckBox1);
container.add(jCheckBox2);
container.add(jCheckBox3);
JButton jButton = new JButton("打印");
jButton.addActionListener(e -> {
System.out.println(jCheckBox1.getText() + "按钮的选中状态" + jCheckBox1.isSelected()); //isSelected()方法返回复选框是否选中的布尔值
System.out.println(jCheckBox2.getText() + "按钮的选中状态" + jCheckBox2.isSelected());
System.out.println(jCheckBox3.getText() + "按钮的选中状态" + jCheckBox3.isSelected());
});
container.add(jButton);
}
public static void main(String[] args) {
new DemoJCheckBox();
}
}
运行结果:
当点击按钮时,三个复选框的选中状态会输出到控制台。
列表组件
下拉列表框——JComboBox类
下拉列表框默认显示一个选项,当点击后显示全部选项,并可以选择一个选项。它可以节省空间而显示多数的选择,是一个比较常用的控件。 下拉列表框用JComboBox表示,它的构造方法如下:
public JComboBox(ComboBoxModel dataModel)
:创建一个基于下拉列表框模型的对象。下拉列表框的数据使用模型中的数据。public JComboBox(Object[] arrayData)
:创建一个基于数组数据的下拉列表框对象。public JComboBox(Vector vector)
:创建一个基于向量的下拉列表框对象。
JComboBox类的常用方法如下:
addItem(Object anObject)
:添加一个选项。getItemCount()
:获取选项个数。getSelectedItem()
:获取被选中的选项。getSelectedIndex()
:获取被选中的选项的索引。setEditable(boolean aFlag)
:设定下拉列表框的选项是否可被编辑。
下面给出三种添加选项的例子:
//使用addItem()方法
JComboBox<String> jComboBox = new JComboBox<>();
jComboBox.addItem("身份证");
jComboBox.addItem("学生证");
jComboBox.addItem("军人证");
container.add(jComboBox);
jComboBox.setBounds(20, 10, 80, 21);
JButton jButton = new JButton("打印");
jButton.setBounds(140,10,60,21);
container.add(jButton);
jButton.addActionListener(e -> System.out.println("选中的项是:" + jComboBox.getSelectedItem() + " 索引值为 " + jComboBox.getSelectedIndex()));
//将对象数组作为参数传入
String[] items = {"身份证", "学生证", "军人证"};
container.add(jComboBox);
jComboBox.setBounds(20, 10, 80, 21);
JButton jButton = new JButton("打印");
jButton.setBounds(140, 10, 60, 21);
container.add(jButton);
jButton.addActionListener(e -> System.out.println("选中的项是:" + jComboBox.getSelectedItem() + " 索引值为 " + jComboBox.getSelectedIndex()));
//使用下拉列表模型
String[] items = {"身份证", "学生证", "军人证"};
ComboBoxModel<String> comboBoxModel = new DefaultComboBoxModel<>(items);
JComboBox<String> jComboBox = new JComboBox<>(comboBoxModel);
//也可以这样使用 jComboBox.setModel(comboBoxModel); 但是这两个用法都不是很常见
jComboBox.setBounds(20, 10, 80, 21);
container.add(jComboBox);
JButton jButton = new JButton("打印");
jButton.setBounds(140, 10, 60, 21);
container.add(jButton);
jButton.addActionListener(e -> System.out.println("选中的项是:" + jComboBox.getSelectedItem() + " 索引值为 " + jComboBox.getSelectedIndex()));
示例代码:
package swing.ListBox;
import javax.swing.*;
import java.awt.*;
public class DemoJComboBox extends JFrame{
//本文件演示如何使用下拉列表框
private DemoJComboBox(){
super("标题");
Container container = getContentPane();
container.setLayout(null);
//下面演示JComboBox的常用方法 addItem()添加组件 getSelectedItem()获取被选中的组件 getSelectedIndex()获取被选中元素的索引值 removeItem()移除项
JComboBox<String> jComboBox = new JComboBox<>();
jComboBox.addItem("身份证");
jComboBox.addItem("学生证");
jComboBox.addItem("军人证");
jComboBox.setEditable(true); //setEditable()方法设置组件是否可编辑 这里设为可编辑
jComboBox.setSelectedIndex(1); //setSelectedIndex()设置默认被选中的标签 还有setSelectedItem()
container.add(jComboBox);
jComboBox.setBounds(20, 10, 80, 21);
JButton jButton = new JButton("打印");
jButton.setBounds(140,10,60,21);
container.add(jButton);
jButton.addActionListener(e -> System.out.println("选中的项是:" + jComboBox.getSelectedItem() + " 索引值为 " + jComboBox.getSelectedIndex()));
setBounds(100, 100, 300, 150);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoJComboBox();
}
}
运行结果:
当点击打印按钮后,下拉列表的选中值会输出到控制台。
列表框——JList类
JList用于创建一个不具有下拉效果的列表,相当于单列的表格。在开始需要指定列表框的长和宽,还需要添加滚动面板,否则选项会溢出。
JList类的常用构造方法如下:
public JList()
:创建一个没有选项的下拉列表。public JList(ListModel\<E\> dataModel)
:基于列表模型创建列表。public JList(final Vector\<? extends E\> listData)
:基于向量创建列表。public JList(Object[] listData)
:基于对象数组创建列表。
下面给出几种创建列表框的方法:
//第一种使用构造方法
String[] elements = {"元素1", "元素2", "元素3", "元素4", "元素5", "元素6", "元素7", "元素8", "元素9", "元素10"};
JList<String> jList = new JList<>(elements);
//第二种使用addElement()方法
DefaultListModel<String> listModel = new DefaultListModel<>();
for (String tmp : elements) listModel.addElement(tmp);
JList<String> jList = new JList<>(listModel);
可以使用下方的代码设置选中方式:
//可以选择通过setSelectionMode()方法设置选择模式
ListSelectionModel.SINGLE_INTERVAL_SELECTION 单选
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION 多选 默认
ListSelectionModel.SINGLE_INTERVAL_SELECTION 邻选
jList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
示例代码:
package swing.ListBox;
import javax.swing.*;
import java.awt.*;
import java.util.List;
public class DemoJList extends JFrame {
//本文件演示如何使用列表框
private DemoJList() {
super("标题");
Container container = getContentPane();
container.setLayout(null);
//因为元素过多而面板过小会造成显示不全 因此最好添加滚动面板
JScrollPane jScrollPane = new JScrollPane(jList);
jScrollPane.setBounds(10, 10, 150, 100);
container.add(jScrollPane);
JButton jButton = new JButton("打印");
jButton.setBounds(200, 60, 60, 21);
jButton.addActionListener(e -> {
List<String> list = jList.getSelectedValuesList();
list.forEach(i -> System.out.print(i + " "));
System.out.println();
System.out.println("------------------------");
});
container.add(jButton);
setBounds(100, 100, 300, 160);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoJList();
}
}
运行结果:
文本组件
文本框——JTextField类
JTextArea类用于显示文本框,用户可以在文本框中输入内容。
JTextArea类的常用构造方法如下:
public JTextField()
:创建一个无默认文本、列宽为0的文本框。public JTextField(String text)
:创建一个默认文本为text的文本框。public JTextField(int columns)
:创建一个默认列宽为columns的文本框。public JTextField(String text, int columns)
:创建一个默认文本为text,列宽为columns的文本框。
JTextArea类的常用方法如下:
public void setText(String text)
:设置文本框的文本。public String getText()
:获取文本框内的文本。public void setSize(int width, int height)
:设置文本框的大小。public void setLocation(int x, int y)
:设置文本框的位置。public void setBounds(int x, int y, int width, int height)
:设置文本框的大小和位置。public void setVisible(boolean b)
:设置文本框是否可见。public void setFont(Font font)
:设置文本框的字体。public void setForeground(Color fg)
:设置字体颜色。public void setBackground(Color bg)
:设置背景颜色。
示例代码:
package swing.TextBox;
import javax.swing.*;
import java.awt.*;
public class DemoJTextField {
public static void main(String[] args) {
//本文件演示如何使用文本域
JFrame jFrame = new JFrame("标题");
Container container = jFrame.getContentPane();
container.setLayout(new FlowLayout());
//这里不演示构造方法了 仅介绍可选的参数
//String text 指定文本初始显示内容
//int columns 指定文本框显示的列宽
JTextField jTextField = new JTextField("这是一个文本框"); //指定文本框初始显示内容
container.add(jTextField);
JButton jButton = new JButton("确认");
jTextField.setFont(new Font("楷体", Font.PLAIN, 30)); //设定字体
jButton.addActionListener(e -> {
System.out.println("文本框内的内容是:" + jTextField.getText()); //getText()方法返回文本内容
jTextField.setText(""); //设置内容 这里选择清空
jTextField.requestFocus(); //设置焦点
});
container.add(jButton);
// jTextField.setColumns(30); //设定列宽
//也可以为文本框添加事件 默认是回车
jTextField.addActionListener(e -> System.out.println("点击了回车 此时文本框的内容是:" + jTextField.getText()));
jFrame.setBounds(100, 100, 300, 100);
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
}
运行结果:
文本域——JTextArea类
文本域相对于文本框在于它有多行,可以包含大量文本。文本域使用JTextArea表示,它的构造方法如下:
public JTextField()
:创建一个无文本的文本域。public JTextField(String text)
:创建一个包含默认文本的文本域。public JTextField(String text, int columns)
:创建一个包含默认文本、指定列宽的文本域。public JTextField(Document doc, String text, int columns)
:创建一个具有文档模型、包含默认文本、指定列宽的文本域。
它的常用方法如下:
setLineWrap(boolean wrap)
:设置文本域的文本内容是否自动换行。append(String str)
:向文本域中添加文本内容。
示例代码:
package swing.TextBox;
import javax.swing.*;
import java.awt.*;
public class DemoJTextArea {
//本文件演示如何使用文本域
public static void main(String[] args) {
JFrame jFrame = new JFrame("标题");
Container container = jFrame.getContentPane();
container.setLayout(new FlowLayout());
//下面演示如何使用文本域 构造方法仍然有可选参数 text rows和columns 分别代表默认显示内容 显示行数和显示列宽
JTextArea jTextArea = new JTextArea(10, 25); //这里选择显示10行20列
jTextArea.setText("这是一个文本域"); //setText()方法可以设置文本域的内容
jTextArea.setFont(new Font("楷体", Font.PLAIN, 15)); //setFont()方法可设置字体
//jTextArea.setRows(); jTextArea.setColumns(); 可设置行和高
//jTextArea.append(); 可追加字符串
//jTextArea.insert(); 可插入字符串
//jTextArea.setLineWrap(); 设置是否换行
//因可能文字的显示范围超出了窗口的显示范围 这里添加滚动面板
JScrollPane jScrollPane = new JScrollPane(jTextArea);
JButton jButton = new JButton("获取");
jButton.addActionListener(e -> {
System.out.println("文本内容是:");
System.out.println(jTextArea.getText()); //getText()方法可以获取文本内容
});
container.add(jScrollPane);
container.add(jButton);
jFrame.setBounds(100, 100, 400, 150);
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
}
表格组件
创建表格——JTable类
表格使用JTable表示。它的构造方法如下:
public JTable(Object[][] rowData, final Object[] columnNames)
:rowData表示存储表格数据的二维数组,columnName代表每一列的名字。
为防止表格溢出,也需要将表格添加到滚动面板中。
示例代码:
package swing.TableBox;
import javax.swing.*;
import java.awt.*;
public class DemoJTable extends JFrame {
//本文件演示如何使用表格文件
private DemoJTable() {
super("标题");
Container container = getContentPane();
container.setLayout(new BorderLayout()); //设为边界布局
//表格的构造方法有几个参数 Object[][] rowData,Object columnNames 分别代表数据和表头 所以下方会创建数据
String[][] data = new String[20][7];
String[] columns = {"A", "B", "C", "D", "E", "F", "G"};
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
data[i][j] = columns[j] + i;
}
}
JTable jTable = new JTable(data, columns);
jTable.setSelectionBackground(Color.GREEN); //setSelectionBackground()方法可以设置被选中数据的背景色
jTable.setSelectionForeground(Color.ORANGE); //setSelectionForeground()方法可以设置被选中数据的前景色 即字体颜色
jTable.setFont(new Font("黑体", Font.PLAIN, 20)); //setFont()方法可以设置表格字体 颜色 风格
jTable.setRowHeight(30); //setRowHeight()方法可以设置行宽
jTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); //setSelectionMode()方法可以设置选择模式 这里选择可多选
jTable.setCellSelectionEnabled(true);
//JTable要配合滚动条使用
JScrollPane jScrollPane = new JScrollPane(jTable);
container.add(jScrollPane);
JButton jButton = new JButton("检测");
jButton.addActionListener(e -> {
//获取表格数据
System.out.println("表格共有" + jTable.getRowCount() + "行" + jTable.getColumnCount() + "列");
//getRowCount() getColumnCount()方法可以获取行列数
System.out.println("表格的第2列名称为:" + jTable.getColumnName(1));
//getColumnName()方法可以获取列名称
System.out.println("表格的第2行第2列名称是:" + jTable.getValueAt(1, 1));
//getValueAt()方法可以获取具体位置的值 需注意这个参数和上面方法的参数都是索引值
System.out.println("第二行第二列元素是否被选中? " + jTable.isCellSelected(1, 1));
});
container.add(jButton, BorderLayout.SOUTH);
setBounds(100, 100, 400, 400);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoJTable();
}
}
运行结果:
当选中第二行第二列后,控制台会输出相关信息。
维护表格——DefaultTableModel类
Java中维护表格的表格模型类有很多,DefaultTableModel是最常用的一个类。它的构造方法如下:
public DefaultTableModel()
:创建一个无初始数据的表格模型。public DefaultTableModel(int rowCount, int columnCount)
:创建一个设定初始列数和行数的表格模型。public DefaultTableModel(Object[] columnNames, int rowCount)
:创建一个具有列名称和指定行数的表格模型。public DefaultTableModel(Object\[]\[] data, Object[] columnNames)
:创建一个已经初始化数据的表格模型。
表格模型被创建后,使用JTable类的构造方法JTable(TableModel dm)就可以创建表格了。处理表格数据时,使用getValueAt()就可以获取单元格数据,使用setValueAt()可以设置单元格数据。
示例代码:
package swing.TableBox;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;
import java.awt.*;
public class DemoJTableModel {
//本文件演示如何维护表格模型 这里使用到了一个类 叫TableModel 它是个接口 它的实现类是AbstractTableModel 它是抽象类 所以用子类DefaultTableModel实例化
//同时本文件也演示如何正确地创建程序
public static void main(String[] args) {
new MyJFrame(); //主文件只创建一个对象即可 其他的操作都在其他方法里实现
}
private static class MyJFrame extends JFrame {
MyJFrame() {
//该构造方法演示了如何创建表格程序
super("维护表格测试程序"); //因为该类继承自JFrame类 因此调用父类构造方法 作用是设置标题
setBounds(400, 300, 500, 300); //该句设置宽高和位置
setMinimumSize(new Dimension(500, 300)); //该语句设置窗口的最小大小 防止窗口过小造成组件挤压引发错误显示
getContentPane().setLayout(new BorderLayout()); //该句设置布局模式为边界布局
String[][] data = {{"A1", "A2"}, {"B1", "B2"}, {"C1", "C2"}}; //数据
String[] columns = {"A", "B"}; //列名
DefaultTableModel tableModel = new DefaultTableModel(data, columns); //参数指定默认行数和列数为3行2列
JTable jTable = new JTable(tableModel); //使用上方的DefaultTableModel对象初始化 因为对表格的单元格操作都是靠模型对象实现的
jTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); //设定表格选中模式为单选
jTable.setRowSorter(new TableRowSorter<>(tableModel)); //setRowSorter()方法可以设置排序方式
JScrollPane jScrollPane = new JScrollPane(jTable); //为表格创建滚动面板 防止表格显示不全
getContentPane().add(jScrollPane, BorderLayout.CENTER); //添加滚动面板
JPanel jPanel = new JPanel(); //新建一个面板 面板将要添加文本框和按钮
getContentPane().add(jPanel, BorderLayout.SOUTH); //将面板放到下方
JLabel jLabel1 = new JLabel("A:"); //提示信息1
JLabel jLabel2 = new JLabel("B:"); //提示信息2
JTextField jTextField1 = new JTextField(10); //文本框A 存储表格左边待填数据 10代表显示的字符数
JTextField jTextField2 = new JTextField(10); //文本框B 存储表格右边待填数据
JButton jButton1 = new JButton("插入"); //插入按钮
jButton1.addActionListener(e -> {
String[] string = {jTextField1.getText(), jTextField2.getText()}; //字符串数组获取文本框内容 作为表格添加行的参数
tableModel.addRow(string); //添加行
jTextField1.setText(""); //清空文本框
jTextField2.setText("");
JOptionPane.showMessageDialog(this, "插入成功!", "提示", JOptionPane.INFORMATION_MESSAGE);
//生成提示对话框
});
jButton1.setSize(30, 21); //设置按钮大小
JButton jButton2 = new JButton("修改"); //修改按钮
jButton2.addActionListener(e -> {
int rowSelected = jTable.getSelectedRow(); //获取选中的列 如此数值为-1则代表没有选中行
if (rowSelected != -1) { //若选中行
String stringA = jTextField1.getText(); //两个字符串对象分别获取文本框内容
String stringB = jTextField2.getText();
jTable.setValueAt(stringA, rowSelected, 0); //实现修改操作
jTable.setValueAt(stringB, rowSelected, 1);
jTextField1.setText(""); //清空文本框
jTextField2.setText("");
JOptionPane.showMessageDialog(this, "修改成功!", "提示", JOptionPane.INFORMATION_MESSAGE);
//生成提示对话框
} else {
JOptionPane.showMessageDialog(this, "修改失败,请先选中行!", "提示", JOptionPane.ERROR_MESSAGE);
//若错误则弹出错误提示
}
});
jButton2.setSize(30, 21);
JButton jButton3 = new JButton("删除"); //删除按钮
jButton3.addActionListener(e -> {
int rowSelected = jTable.getSelectedRow();
if (rowSelected != -1) {
tableModel.removeRow(rowSelected);
JOptionPane.showMessageDialog(this, "删除成功!", "提示", JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(this, "删除失败,请先选中行!", "提示", JOptionPane.ERROR_MESSAGE);
}
});
jButton3.setSize(30, 21);
jPanel.add(jLabel1); //添加组件
jPanel.add(jTextField1);
jPanel.add(jLabel2);
jPanel.add(jTextField2);
jPanel.add(jButton1);
jPanel.add(jButton2);
jPanel.add(jButton3);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true); //设置此窗体可见
}
}
}
运行结果:
在A和B中输入信息后点击插入,会在表格中添加新行:
事件监听
上文中涉及到的组件在点击时是没有任何效果的,需要给组件增加事件监听才可以触发事件。
事件——ActionEvent
ActionEvent是Swing中比较常用的事件监听器,很多组件的动作都可以使用它监听,如按钮被单击。下表描述了动作事件监听器的接口和事件源等。
定义 | 实现方式 |
---|---|
事件名 | ActionEvent |
事件源 | JButton、JList等组件 |
监听接口 | ActionListener |
添加监听方法 | addActionLister() |
删除监听方法 | removeActionListener() |
示例代码:
package swing.ActionListeners;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class DemoEventListener extends JFrame {
//本文件演示如何使用事件监听
//本文件做一个带有多种组件的面板 为每个组件都加上事件监听 注意看代码
private DemoEventListener() {
super("事件监听程序");
setBounds(400, 300, 400, 200); //设置容器大小位置
setMaximumSize(new Dimension(400, 200)); //设置最小大小
//添加一个标签 用于提示事件发生的类型
JPanel jPanel = new JPanel();
jPanel.setLayout(new FlowLayout());
getContentPane().add(jPanel);
JLabel jLabel = new JLabel("无事件发生");
jLabel.setHorizontalAlignment(SwingConstants.CENTER);
getContentPane().add(jLabel, BorderLayout.SOUTH);
//下面添加组件 但不是重点 重点是事件监听
JButton jButton = new JButton("按钮");
jButton.setSize(50, 25);
//为按钮添加事件监听 按钮的事件监听是点击 以下不使用lambda表达式了
jButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { //事件监听类的唯一抽象方法 实现该方法即可完成事件监听 参数ActionEvent就是事件监听类
jLabel.setText("按钮被点击"); //更改标签 提示信息
}
});
jPanel.add(jButton);
JTextField jTextField = new JTextField(10);
jTextField.addActionListener(new ActionListener() { //文本框的事件监听是是否输入回车
public void actionPerformed(ActionEvent e) {
jLabel.setText("文本框输入了回车 此时文本框的内容是:" + jTextField.getText());
}
});
jPanel.add(jTextField);
JCheckBox jCheckBox = new JCheckBox("复选框");
jCheckBox.addActionListener(new ActionListener() { //复选框的事件监听是点击(因为复选框是复选按钮 也是按钮的一种)
public void actionPerformed(ActionEvent e) {
jLabel.setText("复选框改变了选中状态 是否被选中:" + jCheckBox.isSelected());
}
});
jPanel.add(jCheckBox);
JRadioButton jRadioButton = new JRadioButton("单选框"); //单选框的事件监听是点击
jRadioButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jLabel.setText("单选框改变了选中状态 是否被选中:" + jRadioButton.isSelected());
}
});
jPanel.add(jRadioButton);
JComboBox<String> jComboBox = new JComboBox<>(new String[]{"选项1", "选项2", "选项3"}); //下拉列表的事件监听是改变了选项
jComboBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jLabel.setText("下拉列表改变了选中项:" + jComboBox.getSelectedItem());
}
});
jPanel.add(jComboBox);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoEventListener();
}
}
运行结果:
当触发了对应组件的事件后下方会输出相关信息。
键盘事件——KeyEvent
当在文本框内输入内容时,键盘事件就会发生。KeyEvent负责捕获键盘事件,使用addKeyListener()方法就可以添加键盘监听事件。
KeyEventListener接口有三个抽象方法,分别是keyTyped(KeyEvent e)、keyPressed(KeyEvent e)、keyReleased(KeyEvent e),分别代表键盘被敲击、键盘被按下和键盘被松开。
KeyEvent类的常用方法如下:
getSource()
:返回触发事件的组件对象,为Object对象。getKeyChar()
:获取与此次事件有关的键盘按键字符。getKeyCode()
:获取与此次事件有关的键盘按键代码。getKeyText(int keyCode)
:获取与keyCode相对应的键盘按键字面值。isActionKey()
:用于查看触发此事件的按键是否为动作按键。isControlDown()
:用于查看ctrl键是否被按下。isAltDown()
:用于查看Alt键是否被按下。isShiftDown()
:用于查看Shift键是否被按下。
示例代码:
package swing.ActionListeners;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class DemoKeyboardListener extends JFrame {
//本文件演示如何使用键盘监听
//键盘监听常用如下两个方法: getKeyCode()获得键盘释放或按下时的键盘代码 对键盘击键无效 返回一个int值 其实是KeyEvent里的常量 可使用getKeyText()方法转换成字符串
//还有getKeyChar() 获得键盘击键时的字符 注意看下面的代码 这里只演示这两个方法 其他方法仅列出
//getSource()返回触发此次事件的组件对象 为Object类型 需要将其强制转换成正确的组件类型
private DemoKeyboardListener() {
super("键盘监听程序");
setBounds(400, 300, 400, 300);
setResizable(false);
setLayout(null);
JLabel jLabel1 = new JLabel("击键");
jLabel1.setBounds(10, 40, 30, 21);
getContentPane().add(jLabel1);
JLabel jLabel2 = new JLabel("按下");
jLabel2.setBounds(10, 120, 30, 21);
getContentPane().add(jLabel2);
JLabel jLabel3 = new JLabel("释放");
jLabel3.setBounds(10, 200, 30, 21);
getContentPane().add(jLabel3);
JLabel jLabel4 = new JLabel("结果:");
jLabel4.setBounds(160, 10, 30, 21);
getContentPane().add(jLabel4);
JTextArea jTextArea = new JTextArea();
JScrollPane jScrollPane = new JScrollPane(jTextArea);
jScrollPane.setBounds(160, 40, 200, 200);
jTextArea.setEditable(false);
jTextArea.setFont(new Font("微软雅黑", Font.BOLD, 15));
jScrollPane.setBorder(BorderFactory.createLineBorder(Color.GRAY));
getContentPane().add(jScrollPane);
JTextField jTextField1 = new JTextField();
jTextField1.addKeyListener(new KeyListener() { //文本框监听可使用KeyListener类 若是面板则需用KeyAdapter类
public void keyTyped(KeyEvent e) {
jTextArea.append("键盘被击键:" + e.getKeyChar() + "\n");
}
//键盘监听易错易忘点:
//getKeyCode()返回触发事件的关联的整数 这个数是KeyEvent里的VK_常量 它们也是整数值 若想通过此整数获取键盘相关的字符
//需配合getKeyText()方法使用 参数就是这个Code值 且此方法只能在键被释放和键被按下的方法里应用 由于击键的判断逻辑
//getKeyCode()方法在击键的方法里得不到与此事件相关联的值(总是0)
public void keyPressed(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
});
jTextField1.setBounds(50, 40, 100, 21);
getContentPane().add(jTextField1);
JTextField jTextField2 = new JTextField();
jTextField2.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
jTextArea.append("键盘被按下:" + KeyEvent.getKeyText(e.getKeyCode()) + "\n");
}
public void keyReleased(KeyEvent e) {}
});
jTextField2.setBounds(50, 120, 100, 21);
getContentPane().add(jTextField2);
JTextField jTextField3 = new JTextField();
jTextField3.setBounds(50, 200, 100, 21);
jTextField3.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) {}
public void keyReleased(KeyEvent e) {
jTextArea.append("键盘被释放:" + KeyEvent.getKeyText(e.getKeyCode()) + "\n");
}
});
new Thread(() -> {
while (true) {
try {
Thread.sleep(100);
if (jTextField1.getText().length() > 10) jTextField1.setText("");
if (jTextField2.getText().length() > 10) jTextField2.setText("");
if (jTextField3.getText().length() > 10) jTextField3.setText("");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
getContentPane().add(jTextField3);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoKeyboardListener();
}
}
运行结果:
当在对应的文本框内输入时,右边会显示触发的事件信息。
鼠标事件——MouseEvent
鼠标被单击、移入、移出等事件由MouseEvent表示,由MouseListener捕捉。MouseListener有五个抽象方法:mouseClicked(MouseEvent e),在鼠标被单击时触发、mousePressed(MouseEvent e),在鼠标按键被按下时触发、mouseReleased(MouseEvent e),在鼠标按键被松开时触发、mouseEntered(MouseEvent e),在鼠标指针移入组件时被触发、mouseExited(MouseEvent e),在鼠标指针溢出组件时被触发。
MouseEvent类的常用方法如下:
getSource()
:获取触发此次事件的组件对象本身,为Object类型。getButton()
:获取触发此次事件的按键类型的整型值。getClickCount()
:获取鼠标单击的次数。
示例代码:
package swing.ActionListeners;
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class DemoMouseListener extends JFrame {
//本文件演示如何使用鼠标监听
//最好在查看一个事件的效果时注释掉其他 监听的结果在控制台 注意查看
private DemoMouseListener() {
super("鼠标监听程序");
setBounds(400, 300, 400, 300);
setResizable(false);
setLayout(null);
addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) { //mouseClicked()方法监测鼠标单击事件
System.out.println("鼠标点击了");
System.out.println("点击的是" + buttonClicked(e.getButton())); //getButton()方法可以获取哪个键被使用了 返回的是int值 常量
System.out.println("点击了" + e.getClickCount() + "次"); //getClickCount可以获取单击了多少次
}
public void mousePressed(MouseEvent e) { //mousePressed()方法监测鼠标按压事件
System.out.println("鼠标按压:" + buttonClicked(e.getButton())); //注意看下面的成员方法
}
public void mouseReleased(MouseEvent e) { //mouseReleased()方法监测鼠标释放事件
System.out.println("鼠标松开了");
}
public void mouseEntered(MouseEvent e) { //mouseEntered()方法监测鼠标移入组件事件
System.out.println("鼠标移入窗体");
}
public void mouseExited(MouseEvent e) { //mouseExited()方法监测鼠标移出组件事件
System.out.println("鼠标移出窗体");
}
});
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoMouseListener();
}
String buttonClicked(int button) {
switch (button) {
case MouseEvent.BUTTON1: //改代码实现将int值转换成对应的字符串
return "鼠标左键";
case MouseEvent.BUTTON2:
return "鼠标滚轮";
case MouseEvent.BUTTON3:
return "鼠标右键";
}
return "鼠标其他键";
}
}
运行结果:
当鼠标在窗体内外移动、点击时,控制台会输出对应信息:
鼠标移入窗体
鼠标移出窗体
鼠标移入窗体
鼠标按压:鼠标左键
鼠标松开了
鼠标点击了
点击的是鼠标左键
点击了1次
鼠标移出窗体
鼠标移入窗体
鼠标移出窗体
其他组件
菜单栏——JMenuBar类、JMenu类、JMenuItem类
菜单栏和下拉列表框比较类似,只是它会显示在标题下方,窗体的上部分。菜单栏是窗体的一个组件,菜单栏使用JMenuBar表示;一个窗体只能设置一个菜单栏,一个菜单栏可以包含多个菜单项,菜单项用JMenu表示;每一个菜单项相当于一个下拉列表框,而一个菜单项里面可以有很多子项,子项用JMenuItem表示。
JMenuBar类只有一个无参的构造方法。
JMenuBar类的常用成员方法如下:
创建菜单栏对象后,可以使用add(JMenu c)添加一个菜单项。
JMenu类的构造方法如下:
JMenu(String text)
:创建一个带有文本的菜单项。
创建菜单项对象后,可以使用add(JMenuItem menuItem)添加菜单子项。
JMenuItem类的构造方法如下:
public JMenuItem()
:创建一个不带文本的菜单子项。public JMenuItem(Icon icon)
:创建一个带有图标的菜单子项。public JMenuItem(String text)
:创建一个带有文本的菜单子项。public JMenuItem(String text, Icon icon)
:创建一个带有图标和文本的菜单子项。
实例化JMenuItem类后,可以使用addActionListener()方法添加事件监听,实现菜单栏该有的功能。
最后,调用窗体对象的setMenuBar()方法设置菜单栏就可以了。
示例代码:
package swing.OtherSwing;
import javax.swing.*;
public class DemoJMenu extends JFrame {
public DemoJMenu() {
super("菜单栏使用");
setVisible(true);
setBounds(500, 400, 500, 500);
setDefaultCloseOperation(EXIT_ON_CLOSE);
//JMenuItem类:菜单项类,是菜单的子项
JMenuItem jMenuItem1 = new JMenuItem("子项1");
JMenuItem jMenuItem2 = new JMenuItem("子项2");
//可以为此菜单项添加事件监听
jMenuItem2.addActionListener(e -> JOptionPane.showMessageDialog(this, "点击了“子项2”"));
//JMenu可定义一个菜单项,使用此菜单项的add方法添加子项
JMenu jMenu = new JMenu("菜单");
jMenu.add(jMenuItem1);
jMenu.add(jMenuItem2);
//JMenuBar菜单栏类,使用add方法添加菜单,使用窗体的setJMenuBar()方法就可以设置此窗体的菜单栏了
JMenuBar jMenuBar = new JMenuBar();
jMenuBar.add(jMenu);
setJMenuBar(jMenuBar);
}
public static void main(String[] args) {
new DemoJMenu();
}
}
运行结果:
在点击这两个菜单项的第二个后,就会弹出一个对话框显示信息。
滚动条——JSlider类
滚动条具有一段长度,被分为101个节点,100份,可以使用滚动条调节一些数值的百分比。滚动条使用JSlider表示,它的构造方法如下:
public JSlider()
。public JSlider(int orientation)
。public JSlider(int min, int max)
。public JSlider(int min, int max, int value)
。public JSlider(int orientation, int min, int max, int value)
。
参数orientation代表滚动条的方向,默认是水平;min和max指定滚动条的最大和最小长度;value指滚动条刚创建时所在的位置。
JSlider的常用方法如下:
public int getValue()
:返回滚动条目前所在位置。public void setValue(int n)
:设置滚动条所在位置。public void setFont(Font font)
:设置滚动条文字的显示字体。public void addChangeListener(ChangeListener l)
:给滚动条添加事件监听,在滚动条值被改变时触发。
示例代码:
package swing.OtherSwing;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
public class DemoJSlider extends JFrame {
private final JSlider jSlider;
Image img;
public DemoJSlider() {
try {
img = ImageIO.read(new File("src/JavaDraw/img.jpg"));// 读取图片文件
} catch (IOException e) {
e.printStackTrace();
}
CanvasPanel canvas = new CanvasPanel();
jSlider = new JSlider();
jSlider.setMaximum(1000);
jSlider.setValue(100);
jSlider.setMinimum(1);
jSlider.addChangeListener(e -> canvas.repaint());
JPanel center = new JPanel();
center.setLayout(new BorderLayout());
center.add(jSlider, BorderLayout.SOUTH);
center.add(canvas, BorderLayout.CENTER);
setContentPane(center);
setBounds(100, 100, 800, 600); // 设置窗体大小和位置
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗体关闭模式
setTitle("绘制图片");
}
public static void main(String[] args) {
new DemoJSlider().setVisible(true);
}
class CanvasPanel extends JPanel {
public void paint(Graphics g) {
super.paint(g);
int newW, newH;
int imgWidth = img.getWidth(this); // 获取图片宽度
int imgHeight = img.getHeight(this); // 获取图片高度
float value = jSlider.getValue();// 滑块组件的取值
newW = (int) (imgWidth * value / 100);// 计算图片放大后的宽度
newH = (int) (imgHeight * value / 100);// 计算图片放大后的高度
g.drawImage(img, 0, 0, newW, newH, this);// 绘制指定大小的图片
}
}
}
运行结果:
在滑动下方滚动条时,图片的大小也会改变。
进度条——JProgressBar类
进度条有两种状态:具有确切值和不具有确切值。进度条使用JProgressBar表示,它的构造方法如下:
public JProgressBar()
:创建一个最小值为0,最大值为100的进度条。public JProgressBar(int min, int max)
:指定进度条的最大值和最小值。
JProgressBar类的常用方法如下:
setBorderPainted(boolean b)
:设置是否绘制进度条边框。getValue()
:返回进度条此时的值。setValue(int value)
:设置进度条的值。public void setIndeterminate(boolean newValue)
:设置进度条是处于确切值状态还是不确切状态。setOrientation(int newOrientation)
:设置进度条的方向。setString(String s)
:设置进度字符串的值。
在实际使用中,一般使用一个线程来单独控制进度条的值。
使用确切值示例代码如下:
package swing.JProgressBar;
import javax.swing.*;
public class DemoJProgressBar1 extends JFrame {
//本文件演示如何使用带有确切值的进度条 以及一些常用方法
private DemoJProgressBar1() {
super("进度条演示");
setBounds(400, 300, 400, 300);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JProgressBar jProgressBar = new JProgressBar();
//使用不带参数的构造方法就可以创建最小为0最大为100的进度条
jProgressBar.setStringPainted(true); //该方法用于设置进度条是否显示数字
jProgressBar.setBorderPainted(true); //设置 borderPainted 属性,如果进度条应该绘制其边框,则此属性为 true
System.out.println("进度条的最小值为:" + jProgressBar.getMinimum()); //返回进度条的最小值
System.out.println("进度条的最大值为:" + jProgressBar.getMaximum()); //返回进度条的最大值
getContentPane().add(jProgressBar);
Thread t = new Thread(() -> {
for (int i = 0; i <= 100; i++) {
jProgressBar.setValue(i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (jProgressBar.getValue() == 50) { //返回进度条的值
System.out.println("进度条已完成50%");
}
if (jProgressBar.getValue() == 100) { //返回进度条的值
System.out.println("进度条已完成");
}
}
});
t.start();
}
public static void main(String[] args) {
new DemoJProgressBar1().setVisible(true);
}
}
运行结果如下。进度条初始值为0,会随时间逐渐增大。
其他Swing知识
设置窗体UI
默认的Swing窗体风格为Metal类型,可以通过使用UIManager类提供的设置UI的方法改变窗体的外观。
UIManager提供了几个静态方法用于获取和改变外观:
UIManager.getCrossPlatformLookAndFeelClassName()
:获取Java的外观和感觉UIManager.setLookAndFeel()
:设置UI组件的外观。JFrame.setDefaultLookAndFeelDecorated(boolean b)
:改变框架的外观和感觉。
windows环境下可以用下面两种方法使窗口显示windows风格窗口
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
除此之外,还可以利用UIManager将UI改变为如下样式:
外观 | 代码 |
---|---|
Metal风格(默认) | String lookAndFeel ="javax.swing.plaf.metal.MetalLookAndFeel"; UIManager.setLookAndFeel(lookAndFeel); |
Windows风格 | String lookAndFeel ="com.sun.java.swing.plaf.windows.WindowsLookAndFeel"; UIManager.setLookAndFeel(lookAndFeel); |
Windows Classic风格 | String lookAndFeel ="com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"; UIManager.setLookAndFeel(lookAndFeel); |
Motif风格 | String lookAndFeel ="com.sun.java.swing.plaf.motif.MotifLookAndFeel"; UIManager.setLookAndFeel(lookAndFeel); |
Mac风格 (需要在相关的操作系统上方可实现) | String lookAndFeel ="com.sun.java.swing.plaf.mac.MacLookAndFeel"; UIManager.setLookAndFeel(lookAndFeel); |
GTK风格 (需要在相关的操作系统上方可实现) | String lookAndFeel ="com.sun.java.swing.plaf.gtk.GTKLookAndFeel"; UIManager.setLookAndFeel(lookAndFeel); |
可跨平台的默认风格 | String lookAndFeel =UIManager.getCrossPlatformLookAndFeelClassName(); UIManager.setLookAndFeel(lookAndFeel); |
使窗体位于屏幕正中央
使用下面的代码即可实现。
package swing;
import javax.swing.*;
import java.awt.*;
class SetFrameAtCenter extends JFrame { //设置窗体默认显示在屏幕中间
SetFrameAtCenter() {
super("窗口位于屏幕正中央");
int sizeX = 500;
int sizeY = 400;
Toolkit toolkit = Toolkit.getDefaultToolkit(); //获取工具包
Dimension screenSize = toolkit.getScreenSize(); //获取屏幕尺寸
int x = screenSize.width / 2 - sizeX / 2;
int y = screenSize.height / 2 - sizeY / 2;
setBounds(x, y, sizeX, sizeY);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new SetFrameAtCenter();
}
}
销毁窗口
调用窗口对象的dispose()方法即可。
package swing;
import javax.swing.*;
public class JFrameDispose extends JFrame {
//窗口的销毁 使用JFrame对象或子类对象的dispose()方法
JFrameDispose() {
super("自动销毁窗口");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setBounds(300, 200, 100, 100);
JButton jButton = new JButton("点按按钮销毁此窗口");
jButton.addActionListener(e -> dispose());
getContentPane().add(jButton);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new JFrameDispose().setVisible(true);
}
}
Java绘图类
Graphics类
它是所有图形上下文的父类,它提供了一些方法允许绘制图片,包含颜色、粗细、字体、文字、图形等。
Graphics2D类
Graphics2D较Graphics提供了一些更复杂的绘图方法,比如给图片增加模糊、拉伸、放大缩小等操作。一般在绘图之前,都先使用强制转换将Graphics对象转换为Graphics2D对象,进而使用这个对象绘图。
绘图
简单的绘图操作可以使用Graphics类完成,但是若想对图片进行调整,或调整画笔粗细等其他操作就需要使用Graphics2D类。
Graphics2D是推荐使用的绘图类,但是程序设计中的绘图对象都是Graphics类的对象,这时应使用强制转换。
使用Java绘图的步骤,第一步先创建窗体,第二步创建画布,Canvas,第三步使用Graphics对象或者创建Graphics2D对象,第四步使用绘图方法。
绘图的第一要素就是创建画布,Canvas类提供了两个绘图方法,paint(),repaint(),第一个是初次绘图调用的方法,第二个是对图片做修改使用的方法。
Graphics2D特有许多方法,最大特点是draw(),和fill()方法,分别可以绘空心图形和实心图形。
我们在绘图时,不要使用new创建画布,最好自定义一个画布类,继承画布类,然后重写绘图方法,把所有的绘图方法放到自定义画布类,最后在容器里添加此画布即可。
先介绍Graphics类。它提供了如下一些绘图方法:
drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
:画弧,前两个参数是坐标,第三个第四个参数是大小,第三个参数是开始角度,第四个参数是总共画的角度。drawLine(int x1, int y1, int x2, int y2)
:画直线,参数分别是起始位置横纵坐标和终点位置横纵坐标。drawOval(int x, int y, int width, int height)
:画椭圆,前两个参数是坐标,后两个参数是大小。drawRect(int x, int y, int width, int height)
:画矩形,前两个参数是坐标,后两个参数是大小。drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
:画圆角矩形,前两个参数是坐标,中间两个参数是大小,最后两个参数是角的角度。drawPolygon(int xPoints[], int yPoints[], int nPoints)
:画多边形,第一个参数是横坐标数组,第二个参数是纵坐标数组,第三个参数是多边形的顶点数。fillArc(int x, int y, int width, int height, int startAngle, int arcAngle)
:画实心弧,前两个参数是坐标,第三个第四个参数是大小,第三个参数是开始角度,第四个参数是总共画的角度,所以这里应是画到270度。drawPolyline(int xPoints[], int yPoints[], int nPoints)
:画多直线,第一个参数是顶点横坐标数组,第二个参数是顶点纵坐标数组,第三个参数是定点数。fillOval(int x, int y, int width, int height)
:画实心椭圆,前两个参数是坐标,后两个参数是大小。fillRect(int x, int y, int width, int height)
:画实心矩形,前两个参数是坐标,后两个参数是大小。fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
画实心圆角矩形,前两个参数是坐标,中间两个参数是大小,最后两个参数是角的角度。fillPolygon(int xPoints[], int yPoints[], int nPoints)
:画实心多边形,第一个参数是横坐标数组,第二个参数是纵坐标数组,第三个参数是多边形的顶点数。
示例代码如下:
package JavaDraw;
import javax.swing.*;
import java.awt.*;
public class DemoGraphics2D extends JFrame {
//简单的绘图操作可以使用Graphics类完成 但是若想对图片进行调整 或调整画笔粗细等其他操作就需要使用Graphics2D类
//Graphics2D是推荐使用的绘图类 但是程序设计中的绘图对象都是Graphics类的对象 这时应使用强制转换
//使用Java绘图的步骤 第一步先创建窗体 第二步创建画布 Canvas 第三步使用Graphics对象或者创建Graphics2D对象 第四步使用绘图方法
//绘图的第一要素就是创建画布 Canvas类提供了两个绘图方法 paint() repaint() 第一个是初次绘图调用的方法 第二个是对图片做修改使用的方法
//Graphics2D特有许多方法 最大特点是draw() 和fill()方法 分别可以绘空心图形和实心图形
//我们在绘图时 不要使用new创建画布 最好自定义一个画布类 继承画布类 然后重写绘图方法 把所有的绘图方法放到自定义画布类 最后在容器里添加此画布即可
DemoGraphics2D() {
super("Java绘图");
setBounds(400, 300, 420, 200); //设置容器大小位置
add(new MyCanvas());
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoGraphics2D();
}
private class MyCanvas extends Canvas { //自定义画布类 继承画布类
public void paint(Graphics g) { //这里重写paint()绘图方法
g.drawArc(10, 10, 40, 40, 90, 180);
//画弧 前两个参数是坐标 第三个第四个参数是大小 第三个参数是开始角度 第四个参数是总共画的角度 所以这里应是画到270度
g.drawLine(60, 10, 100, 50);
//画直线 参数分别是起始位置横纵坐标和终点位置横纵坐标
g.drawOval(110, 10, 60, 40);
//画椭圆 前两个参数是坐标 后两个参数是大小
g.drawRect(180, 10, 60, 40);
//画矩形 前两个参数是坐标 后两个参数是大小
g.drawRoundRect(250, 10, 60, 40, 10, 10);
//画圆角矩形 前两个参数是坐标 中间两个参数是大小 最后两个参数是角的角度
g.drawPolygon(new int[]{320, 390, 320, 390}, new int[]{10, 10, 60, 60}, 4);
//画多边形 第一个参数是横坐标数组 第二个参数是纵坐标数组 第三个参数是多边形的顶点数
g.fillArc(10, 80, 40, 40, 90, 180);
//画实心弧 前两个参数是坐标 第三个第四个参数是大小 第三个参数是开始角度 第四个参数是总共画的角度 所以这里应是画到270度
g.drawPolyline(new int[]{50, 90, 50, 90}, new int[]{80, 80, 140, 140}, 4);
//画多直线 第一个参数是顶点横坐标数组 第二个参数是顶点纵坐标数组 第三个参数是定点数
g.fillOval(110, 80, 60, 40);
//画实心椭圆 前两个参数是坐标 后两个参数是大小
g.fillRect(180, 80, 60, 40);
//画实心矩形 前两个参数是坐标 后两个参数是大小
g.fillRoundRect(250, 80, 60, 40, 10, 10);
//画实心圆角矩形 前两个参数是坐标 中间两个参数是大小 最后两个参数是角的角度
g.fillPolygon(new int[]{320, 390, 320, 390}, new int[]{80, 80, 140, 140}, 4);
}
}
}
运行结果如下:
Graphics2D类继承自Graphics类,它添加了更多功能,在绘图时一般使用该类。它可以使用具体的图形类绘制不同的形状:
- Arcs2D。
- CubicCurve2D。
- Ellipse2D。
- Line2D。
- Point2D。
- QuadCurve2D。
- Rectangle2D。
- RoundRectangle2D。
实例化这些对象后,使用Graphics2D对象的draw(Shape s)或fill(Shape s)即可绘制这些图形。
设置画笔和颜色
画笔——Stroke类
默认情况下,Graphics类使用1像素宽的线条绘制图形,而Graphics2D可以通过更改画笔绘制更多形状的图形。
使用Graphics2D的setStroke(Stroke stroke)来更改画笔。其中Stroke参数代表实现了Stroke接口的画笔类。Java提供了一个最基本的BasicStroke画笔类,可以通过构造方法来初始化画笔的形状:
BasicStroke()
。BasicStroke(float width)
。BasicStroke(float width, int cap, int join)
。BasicStroke(float width, int cap, int join, float miterlimit)
。BasicStroke(float width, int cap, int join, float miterlimit, float[] dash, float dash_phase)
。
参数说明如下:
- width:画笔宽度,必须大于等于0.0f。
- cap:线端点的装饰,有以下几个可选值:
- CAP_BUTT:无装饰,长度最短。
- CAP_ROUND:用半圆装饰端点。
- CAP_SQUARE:用正方形装饰端点,实际长度较CAP_BUTT要长一些。
- join:线段交点处的装饰,有以下几个可选值:
- JOIN_BEVEL:交点处会有一半角度的过渡。
- JOIN_MITER:交点处无过渡。
- JOIN_ROUND:交点处使用圆弧过渡。
- miterlimit:斜接处的剪裁限制,必须大于等于0.0f。
- dash:表示虚线模式的数组。
- dash_phase:开始虚线模式的偏移量。
示例代码在下方。
颜色——Color类
Color类支持任意颜色,不管该平台是否支持该颜色。Color类的构造方法如下:
Color(inr r, int g, int b)
。Color(int rgb)
。
参数说明如下:
- r:三原色中红色的取值。
- g:三原色中绿色的取值。
- b:三原色中蓝色的取值。
- rgb:红绿蓝三原色的总值,可用一个十六进制数字表示。
Color定义了一些色彩的常量,如Color.black代表黑色,Color.white代表白色。
绘图或Swing组件可以使用setColor(Color color)
来设置颜色。
画笔和颜色的示例代码如下:
package JavaDraw;
import javax.swing.*;
import java.awt.*;
public class DemoColorAndStroke extends JFrame {
public DemoColorAndStroke() {
super("画笔和颜色程序");
setBounds(400, 300, 420, 200); //设置容器大小位置
add(new MyCanvas());
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DemoColorAndStroke();
}
private class MyCanvas extends Canvas { //自定义画布类 继承画布类
public void paint(Graphics g) { //这里重写paint()绘图方法
Graphics2D g2D = (Graphics2D) g; //将Graphics对象转换成Graphics2D对象以使用更强大的绘图方法
g2D.drawLine(10, 10, 390, 10); //第一个直线 没有修改 作对比
//颜色类有两个构造方法 三个参数的分别是三原色红黄蓝的取值 一个参数的是色彩对应的数字 也可使用常量初始化
Color color1 = new Color(125, 0, 0);
Color color2 = new Color(125000);
//这里使用常量初始化
Color color = Color.ORANGE;
//要先改变颜色再使用draw()方法
g2D.setColor(color);
g2D.drawLine(10, 30, 390, 30); //第二个直线 橘色
//Stroke类 画笔类 可以调整画笔的大小和粗细
//第一个参数是画笔粗细 单位是像素
//第二个参数是线条端的修饰 CAP_BUTT默认 不加修饰 CAP_ROUND 圆角线头 CAP_SQUARE 会在线条端加一个正方形 看起来仅仅加长了线条 效果不明显
//第三个参数是线条交汇处的修饰 JOIN_MITER交汇处是尖的 JOIN_ROUND交汇处是圆的 JOIN_BEVEL 交汇处是一半一半过度的
Stroke stroke = new BasicStroke(15, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
//使用setStroke()方法可以修改画笔
g2D.setStroke(stroke);
int[] x = {10, 390, 390, 10};
int[] y = {70, 70, 110, 110};
g2D.drawPolyline(x, y, 4); //第三个直线 橘色 线条宽度是15像素 线条端和线条交汇处是圆的
}
}
}
运行结果如下:
绘制文本
设置字体——Font类
在绘制文本之前可以指定文本的字体,使用setFont(Font font)
来改变文本的样式、大小、颜色。
Font类的构造方法如下:
Font(String name, int style, int size)
。
参数说明如下:
name:字体的名称。
style:字体的样式,可选值如下:
- Font.PLAIN:普通样式。
- Font.BOLD:粗体。
- Font.ITALIC:斜体。
size:字体的大小。
显示文字
使用Graphics2D的drawString(str, x, y)
方法即可绘制文本,其中需要传递一个字符串。x,y代表绘制的位置。
示例代码:
package JavaDraw;
import javax.swing.*;
import java.awt.*;
public class DrawWords extends JFrame{
public DrawWords() {
super("画笔和颜色程序");
setBounds(400, 300, 420, 200); //设置容器大小位置
add(new MyCanvas());
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new DrawWords();
}
private class MyCanvas extends Canvas { //自定义画布类 继承画布类
public void paint(Graphics g) { //这里重写paint()绘图方法
Graphics2D g2D = (Graphics2D) g; //将Graphics对象转换成Graphics2D对象以使用更强大的绘图方法
g2D.drawString("绘制文本",10,20); //drawString()方法可以绘制文本 第一个参数是绘制的字符串 后两个参数是位置
//也可以给绘制的文本设置字体和大小 颜色 样式
//这里需要用到字体类
//第一个参数是字符串 代表电脑内已安装的字体 第二个参数是字体样式 PLAIN为基本样式 BOLD为加粗 ITALIC为斜体 也可设置组合
//第三个参数为字体大小
Font font = new Font("微软雅黑",Font.BOLD,20); //比如选择加粗 20号字
g2D.setFont(font); //setFont()方法可以设置字体
g2D.drawString("绘制文本",10,45);
//还可设置颜色 仍然使用Color类
Color color = Color.GREEN;
g2D.setColor(color);
g2D.drawString("绘制文本",10,70);
}
}
}
运行结果如下:
绘制图片
显示图片
使用Graphics2D对象的drawImage(img, x, y, observer)
即可绘制图片。参数说明如下:
- img:Image对象,要绘制的图片。
- x、y:位置。
- observer:绘制图片时要通知的图像观察者。
示例代码:
package JavaDraw;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
public class DrawImage extends JFrame {
private Image img;// 展示的图片
public DrawImage() {
super("绘制图片");
try {
img = ImageIO.read(new File("src/JavaDraw/img.jpg"));// 读取图片文件
} catch (IOException e) {
e.printStackTrace();
}
setBounds(440, 300, 400, 300); // 设置窗体大小和位置
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗体关闭模式
add(new CanvasPanel()); // 设置窗体面板为绘图面板对象
}
public static void main(String[] args) {
new DrawImage().setVisible(true);
}
class CanvasPanel extends JPanel {
public void paint(Graphics g) {
g.drawImage(img, 0, 0, this); // 显示图片
}
}
}
运行结果:
图像处理
放大与缩小
使用drawImage()的重载方法即可绘制缩放后的图片:
drawImage(Image img, int x, int y, int width, int height, mageObserver)
示例代码:
package JavaDraw;
import javax.swing.*;
import java.awt.*;
public class PictureZoom extends JFrame {
private final Image img = new ImageIcon("src/JavaDraw/img.jpg").getImage();
private final CanvasPanel canvasPanel = new CanvasPanel();
private int zoomSize = 0;
PictureZoom() {
super("绘制图片");
getContentPane().setLayout(new BorderLayout());
getContentPane().add(canvasPanel);
JPanel jPanel = new JPanel();
setBounds(440, 300, 400, 300); // 设置窗体大小和位置
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗体关闭模式
add(canvasPanel); // 设置窗体面板为绘图面板对象
//我们使用点击按钮实现改变图片大小 注意在上方定义了缩放比例的全局变量
JButton jButton1 = new JButton("放大(+)");
jButton1.addActionListener(e -> {
zoomSize += 20;
canvasPanel.repaint(); //注意这里要在改变变量之后重新绘图 相当于刷新
});
jPanel.add(jButton1);
JButton jButton2 = new JButton("缩小(-)");
jButton2.addActionListener(e -> {
zoomSize -= 20;
canvasPanel.repaint();
});
jPanel.add(jButton2);
add(jPanel, BorderLayout.SOUTH);
setVisible(true);
}
public static void main(String[] args) {
new PictureZoom();
}
class CanvasPanel extends JPanel {
public void paint(Graphics g) {
super.paint(g); //若图片处理之后还有原图片 就在此处先调用父类的绘画方法
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(img, 0, 0,
img.getWidth(this) + zoomSize, img.getHeight(this) + zoomSize,
this); // 显示图片
}
}
}
运行结果:
当点击放大或缩小后,图片会进行缩放。
图像翻转
实现图像反转需要使用drawImage()的另一个重载方法:
drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer)
参数说明如下:
- img:要绘制的指定图像。
- dx1:目标矩形的第一个角的x坐标。
- dy1:目标矩形第一个角的y坐标。
- dx2:目标矩形的第二个角的x坐标。
- dy2:目标矩形第二个角的y坐标。
- sx1:源矩形的第一个角的x坐标。
- sy1:源矩形第一个角的y坐标。
- sx2:源矩形的第二个角的x坐标。
- sy2:源矩形第二个角的y坐标。
- observer:当更多的图像被缩放和转换时被通知的对象。
示例代码:
package JavaDraw;
import javax.swing.*;
import java.awt.*;
public class PictureOverturn extends JFrame {
private final Image img = new ImageIcon("src/JavaDraw/img.jpg").getImage();
private int dx1;
private int dy1;
private int dx2;
private int dy2; //此处表示图片的目标矩形坐标 分别代表图片目标矩形的左上横纵坐标和右下的横纵坐标
private int sx1, sy1, sx2, sy2; //此处表示图片的源矩形坐标 分别代表图片源矩形的左上横纵坐标和右下的横纵坐标
private int width;
private int height;
private final CanvasPanel canvasPanel = new CanvasPanel();
PictureOverturn() {
super("绘制图片");
getContentPane().setLayout(new BorderLayout());
getContentPane().add(canvasPanel);
setBounds(440, 300, 400, 300); // 设置窗体大小和位置
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗体关闭模式
setVisible(true);
width = img.getWidth(this);
height = img.getHeight(this);
//下面代码搞定目标矩形和原矩形的相关信息
sx2 = dx2 = width;
sy2 = dy2 = height;
JPanel jPanel = new JPanel();
jPanel.setLayout(new FlowLayout());
JButton jButton1 = new JButton("水平翻转");
jButton1.addActionListener(e -> {
sx1 = Math.abs(sx1 - width);
sx2 = Math.abs(sx2 - width);
canvasPanel.repaint();
});
jPanel.add(jButton1);
JButton jButton2 = new JButton("竖直翻转");
jButton2.addActionListener(e -> {
sy1 = Math.abs(sy1 - height);
sy2 = Math.abs(sy2 - height);
canvasPanel.repaint();
});
jPanel.add(jButton2);
getContentPane().add(jPanel, BorderLayout.SOUTH);
}
public static void main(String[] args) {
new PictureOverturn();
}
class CanvasPanel extends JPanel {
public void paint(Graphics g) {
super.paint(g); //若图片处理之后还有原图片 就在此处先调用父类的绘画方法
g.drawImage(img,
dx1, dy1, dx2, dy2,
sx1, sy1, sx2, sy2,
this); // 显示图片
}
}
}
运行结果如下:
当点击水平翻转或垂直翻转后,图片会发生改变。
图片旋转
设置图片旋转需要调用Graphics2D的rotate(double theta)
方法。theta为要旋转的弧度。
示例代码:
package JavaDraw;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class PictureRotate extends JFrame {
private final Image img = new ImageIcon("src/JavaDraw/img.jpg").getImage();
private double angle;
PictureRotate() {
super("绘制图片");
getContentPane().setLayout(new BorderLayout());
CanvasPanel canvasPanel = new CanvasPanel();
getContentPane().add(canvasPanel);
setBounds(440, 300, 400, 300); // 设置窗体大小和位置
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗体关闭模式
setVisible(true);
canvasPanel.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
angle += Math.toRadians(5);
repaint();
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
});
}
public static void main(String[] args) {
new PictureRotate();
}
class CanvasPanel extends JPanel {
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.rotate(angle);
g.drawImage(img, 0, 0, this); // 显示图片
}
}
}
运行结果如下。当点击图片后图片会旋转。
图片倾斜
使用Graphics2D的shear(double shx, double shy)
来倾斜图片。shx、shy分别为对应方向的倾斜度。
示例代码:
package JavaDraw;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class PictureShear extends JFrame {
private final Image img = new ImageIcon("src/JavaDraw/img.jpg").getImage();
private double angleH; //水平倾斜角度
private double angleV; //竖直倾斜角度
PictureShear() {
super("绘制图片");
getContentPane().setLayout(new BorderLayout());
CanvasPanel canvasPanel = new CanvasPanel();
getContentPane().add(canvasPanel);
setBounds(440, 300, 1000, 800); // 设置窗体大小和位置
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗体关闭模式
setVisible(true);
addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
angleV -= Math.toRadians(5);
break;
case KeyEvent.VK_DOWN:
angleV += Math.toRadians(5);
break;
case KeyEvent.VK_LEFT:
angleH -= Math.toRadians(5);
break;
case KeyEvent.VK_RIGHT:
angleH += Math.toRadians(5);
break;
}
canvasPanel.repaint();
}
public void keyReleased(KeyEvent e) {
}
});
}
public static void main(String[] args) {
new PictureShear();
}
class CanvasPanel extends JPanel {
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.shear(angleH, angleV);
g2.drawImage(img, 100, 100,this); // 显示图片
}
}
}
运行结果如下。当按下键盘的上下左右键时,图片会向对应的方向倾斜。