java复习_进阶
Java复习
本文主要进行java的复习与进阶,复习部分参考java基础,这里只包含一些常用的API,进阶部分预计包含javaBean,反射,泛型,注解,集合,IO
API
math.API
BigDecimal
public class LearnString_A {
public static void main(String[] args) {
BigDec big_data = new BigDec();
System.out.println(big_data.do_func());
}
}
class BigDec {
public double do_func(){
BigDecimal a = new BigDecimal("2.0008");
BigDecimal b = new BigDecimal("1.00009");
BigDecimal res = a.subtract(b);
//result.setScale(1,BigDecimal.ROUND_HALF_UP);//四舍五入
return res.doubleValue();
}
}
util.API
Scanner
Scanner输入与hasNext缓冲区输入判断
toCharArray进行字符串分割与Character的字符判断
public void CharSplit(){
Scanner scanner = new Scanner(System.in);
if (scanner.hasNext()){
String input_data = scanner.next();
for (char c: input_data.toCharArray()){
// System.out.println(c);
if (Character.getType(c)==Character.OTHER_LETTER){
System.out.println("chinese");
}else if (Character.isDigit(c)){
System.out.println(String.format("%-8d",Integer.parseInt(String.valueOf(c))));
}else if(Character.isLetter(c)){
System.out.println(String.format("%-8s", "letter"));
}else {
System.out.println(String.format("%-10s", "other character"));
}
}
scanner.close();
}
String
String不可变
StringBuilder
可变对象,预分配缓冲区,向StringBuilder新增字符时,不会创建新的临时对象
StingBuilder支持链式操作,使用append进行拼接,每个函数return this -> getValue()
StringJoiner
可以指定开头和结尾,类似于python的join
public String StrConcat() {
String[] names = {"Bob", "Alice", "Smith"};
StringJoiner origin_str = new StringJoiner(", ","Hello","!~~");
for (String name:names){
origin_str.add(name);
}
return origin_str.toString();
}
javaBean
BeanInfo
public class BeanLearn {
public static void main(String[] args) throws Exception {
BeanInfo info = Introspector.getBeanInfo(Person.class);
for(PropertyDescriptor pd:info.getPropertyDescriptors()){
System.out.println(pd.getName());
System.out.println(pd.getReadMethod());
System.out.println(pd.getWriteMethod());
}
}
}
classpath和jar
classpath
JVM环境变量,用于解决类依赖(后续有maven来解决此种依赖)
java -classpath .;system_path java -cp ,;system_path
原则上不支持设置classpath,只有在单独需求运行某个java文件和其依赖时再使用
jar
由于jar包实际上就是zip包,可以直接打压缩,后缀名zip改为jar就可以创建一个jar包
关于具体的jar包的格式,只需要注意,bin目录不能作为第一级别目录,打包jar包可以使用IDEA实现
java -jar hello.jar运行jar包
内部类
内部类拥有外部类的访问权限,且可以变相实现多重继承
public class OuterClass {
private String name;
private int age;
class InnerClass {
public InnerClass() {
name = "Mr.Wu";
age = 25;
}
}
}
反射,主界,泛型集合与IO
反射
访问字段
- Field getField(name):根据字段名获取某个public的field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
- Field[] getFields():获取所有public的field(包括父类)
- Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
一个Field对象包含了一个字段的所有信息:
getName():返回字段名称,例如,"name";getType():返回字段类型,也是一个Class实例,例如,String.class;getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
public static void main(String[] args) throws NoSuchFieldException {
Class<Student> stdClass = Student.class;
System.out.println(stdClass.getField("name"));
System.out.println(stdClass.getField("score"));
System.out.println(stdClass.getDeclaredField("grade"));
System.out.println(Arrays.toString(stdClass.getFields()));
}
class Person{
public String name;
}
class Student extends Person{
public int score;
private int grade;
}
//类对象+类属性对象 -> 类属性对象绑定实例 -> 得到值
Class<Student> stdClass = Student.class;
Object stu1 = new Student();
Field fie = stdClass.getField("name");
fie.setAccessible(true);
Object value = fie.get(stu1);
System.out.println(value);
fie.set(stu1,123);
System.out.println(fie.get(stu1));
此外,setAccessible(true)可能会失败。如果JVM运行期存在SecurityManager,那么它会根据规则进行检查,有可能阻止setAccessible(true)。例如,某个SecurityManager可能不允许对java和javax开头的package的类调用setAccessible(true),这样可以保证JVM核心库的安全
访问方法
Method getMethod(name, Class...):获取某个public的Method(包括父类)Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)Method[] getMethods():获取所有public的Method(包括父类)Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)
一个Method对象包含一个方法的所有信息:
getName():返回方法名称,例如:"getScore";getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
public static void main(String[] args) throws Exception {
Class<Student> stdClass = Student.class;
// 获取public方法getScore,参数为String:
System.out.println(Arrays.toString(stdClass.getMethod("getScore", String.class).getParameterTypes()));
// 获取继承的public方法getName,无参数:
System.out.println(stdClass.getMethod("getName").getReturnType());
// 获取private方法getGrade,参数为int:
System.out.println(stdClass.getDeclaredMethod("getGrade", int.class));
}
//通过反射调用方法 方法对象+类对象 -> 方法对象绑定实例 -> 得到结果
Class<Student> stdClass = Student.class;
Object stu = new Student();
Method method = stdClass.getMethod("getScore", String.class);
Method.setAccessible(true)
// 静态方法时使用null来代替stu对象(因为静态方法无法new获取,直接通过method即可绑定)
char[] res = (char[]) method.invoke(stu,"test");
System.out.println(res.getClass());
//
class Student extends Person {
public int getScore(String type) {
return 99;
}
private int getGrade(int year) {
return 1;
}
}
class Person {
public String getName() {
return "Person";
}
}
注:仍然遵循多态原则
调用构造方法
注意:只能调用无参构造方法
Person p = Person.class.newInstance();
getConstructor(Class...):获取某个public的Constructor;getDeclaredConstructor(Class...):获取某个Constructor;getConstructors():获取所有public的Constructor;getDeclaredConstructors():获取所有Constructor。
Constructor<Student> cons1 = Student.class.getConstructor(String.class, String.class);
Student stu = (Student)cons1.newInstance("2233","213");
System.out.println(stu.getGrade());
class Student extends Person {
private final String score;
private final String grade;
public Student(String score, String grade) {
super();
this.score = score;
this.grade = grade;
}
public String getScore() {
return score;
}
public String getGrade() {
return grade;
}
}
获取父类对象
Class<? super Student> n = stdClass.getSuperclass();
System.out.println(n);//class Person
System.out.println(n.getSuperclass());//class java.lang.Object
// 获取接口,只返回当前类的,无父类实现的接口
Class s = Integer.class;
Class[] is = s.getInterfaces();
如果是两个Class实例,要判断一个向上转型是否成立,可以调用isAssignableFrom():
Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer
Java注解
元注解
最常用的元注解是@Target。使用@Target可以定义Annotation能够被应用于源码的哪些位置:
- 类或接口:
ElementType.TYPE; - 字段:
ElementType.FIELD; - 方法:
ElementType.METHOD; - 构造方法:
ElementType.CONSTRUCTOR; - 方法参数:
ElementType.PARAMETER。
另一个重要的元注解@Retention定义了Annotation的生命周期:
- 仅编译期:
RetentionPolicy.SOURCE; - 仅class文件:
RetentionPolicy.CLASS; - 运行期:
RetentionPolicy.RUNTIME。
使用@Repeatable这个元注解可以定义Annotation是否可重复。
使用@Inherited定义子类是否可继承父类定义的Annotation。@Inherited仅针对@Target(ElementType.TYPE)类型的annotation有效,并且仅针对class的继承,对interface的继承无效:
处理注解
因为注解定义后也是一种class,所有的注解都继承自java.lang.annotation.Annotation,因此,读取注解,需要使用反射API。
Java提供的使用反射API读取Annotation的方法包括:
判断某个注解是否存在于Class、Field、Method或Constructor:
Class.isAnnotationPresent(Class)Field.isAnnotationPresent(Class)Method.isAnnotationPresent(Class)Constructor.isAnnotationPresent(Class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
int min() default 0;
int max() default 255;
}
// javaBean
public class Person {
@Range(min=1, max=20)
public String name;
@Range(max=10)
public String city;
}
// 自定义检查
void check(Person person) throws IllegalArgumentException, ReflectiveOperationException {
// 遍历所有Field:
for (Field field : person.getClass().getFields()) {
// 获取Field定义的@Range:
Range range = field.getAnnotation(Range.class);
// 如果@Range存在:
if (range != null) {
// 获取Field的值:
Object value = field.get(person);
// 如果值是String:
if (value instanceof String) {
String s = (String) value;
// 判断值是否满足@Range的min/max:
if (s.length() < range.min() || s.length() > range.max()) {
throw new IllegalArgumentException("Invalid field: " + field.getName());
}
}
}
}
}
Java泛型
泛型定义与使用 :略
泛型的坑
由于泛型在编译时进行擦拭,即将T -> Object 所以理论上对Object进行的操作都不能直接对泛型使用
- Object无法对基本类型(int)进行继承或操作
- 理论上没有必要通过反射获取Object(无法获取泛型类型)
- 理论上不能对Object进行实例化
- 不能进行泛型的覆写override(泛型调用时方法名不能为内置方法)
- 可以继承泛型类
public class TObject {
public static void main(String[] args) {
// Pair<int>pair = new Pair<>(1,2) wrong 泛型无法定义基本类型<Object>
Pair<Integer>pair = new Pair<>(1,2);
// 无法通过反射获取到对象的泛型
System.out.println(pair.getClass());
System.out.println(Pair.class);
// 无法判断带泛型的实例类
System.out.println(pair instanceof Pair<Integer>);//exception
// 通过反射来获取父类的泛型的类型
// 注意,Type-> Class + ParameterizedType + GenericArrayType + WildcardType
InPair ip = new InPair(1,2);
Class<InPair> inPairClass = InPair.class;
// 获取父类的Type对象,这里多做一遍的目的是为了防止没有父类(即不是继承类的情况)
Type t = inPairClass.getGenericSuperclass();
if (t instanceof ParameterizedType){
// 向下转型为ParameterizedType
ParameterizedType pt = (ParameterizedType) t;
// 获取到每个参数的Type对象(泛型可以有多个,如Map)
Type[] types = pt.getActualTypeArguments();
Type firstType = types[0];
// 向下转型为Class,这里可以不用转型直接输出
Class<?> typeClass = (Class<?>) firstType;
System.out.println(typeClass);
System.out.println(Arrays.toString(types));
}
// 简便方式
Type[] types = ((ParameterizedType) t).getActualTypeArguments();
System.out.println(Arrays.toString(types));
}
}
class Pair<T> {
private T first;
private T last;
public Pair() {
}
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public T getLast() {
return last;
}
// wrong 不能对泛型进行实例化
public void defineTObject(){
this.first = new T();
}
}
class InPair extends Pair<Integer>{
public InPair() {
super();
}
public InPair(Integer first, Integer last) {
super(first, last);
}
}
集合
Iterable
public class userCollection {
public static void main(String[] args) {
UseIterate user_define_iterate = new UseIterate();
Integer[] array = {1,2,3,4};
List<Integer> user_list = Arrays.asList(array);
user_define_iterate.getUser_list(user_list);
}
}
class UseIterate {
public static boolean isNull(List<?> p) {
return p.isEmpty() || !(p instanceof ArrayList);
}
public <T> void getUser_list(List<? super T> user_list) {
if (isNull(user_list)) {
/*for (Object ll : user_list) {
System.out.println(ll);
}*/
for(Iterator it_value =user_list.listIterator();it_value.hasNext();){
System.out.println(it_value.next());
}
}
else {
System.out.println("null");
}
}
}
- Collection顶层借口,定义集合的约定
- List也是顶层借口,继承了Collection接口,也是ArrayList LinkedList等的父类
- Set Queue 也继承了Collection接口,用法见下方
- Map为key,value存储的对象
ArrayList
ArrayList是实现了List接口的动态扩容数组,完全代替vector,除了其不是线程安全的这一点
线程安全的List Collections.synchronizedList-> List list = Collections.synchronizedList(new ArrayList(....))
public<T> void setSyn_safe_list(List<T> syn_safe_list) {
this.syn_safe_list = Collections.synchronizedList(new ArrayList<>(syn_safe_list));
}
编写equals来进行contains判断
- 自反性(Reflexive):对于非
null的x来说,x.equals(x)必须返回true; - 对称性(Symmetric):对于非
null的x和y来说,如果x.equals(y)为true,则y.equals(x)也必须为true; - 传递性(Transitive):对于非
null的x、y和z来说,如果x.equals(y)为true,y.equals(z)也为true,那么x.equals(z)也必须为true; - 一致性(Consistent):对于非
null的x和y来说,只要x和y状态不变,则x.equals(y)总是一致地返回true或者false; - 对
null的比较:即x.equals(null)永远返回false。
规则
- 先确定实例“相等”的逻辑,即哪些字段相等,就认为实例相等;
- 用
instanceof判断传入的待比较的Object是不是当前类型,如果是,继续比较,否则,返回false; - 对引用类型用
Objects.equals()比较,对基本类型直接用==比较
public boolean equals(Object o) {
if (o instanceof Person) {
Person p = (Person) o;
return Objects.equals(this.firstName, p.firstName) && Objects.equals(this.lastName, p.lastName) && this.age == p.age;
}
return false;
}
Vector
Vector是一个线程安全的容器,对内部的每个方法都简单粗暴的上锁,因此,访问效率远远低于ArrayList
其扩容为一倍扩容,而ArrayList为0.5倍扩容
Stack
注意到Deque接口实际上扩展自Queue
| Queue | Deque | |
|---|---|---|
| 添加元素到队尾 | add(E e) / offer(E e) | addLast(E e) / offerLast(E e) |
| 取队首元素并删除 | E remove() / E poll() | E removeFirst() / E pollFirst() |
| 取队首元素但不删除 | E element() / E peek() | E getFirst() / E peekFirst() |
| 添加元素到队首 | 无 | addFirst(E e) / offerFirst(E e) |
| 取队尾元素并删除 | 无 | E removeLast() / E pollLast() |
| 取队尾元素但不删除 | 无 | E getLast() / E peekLast() |
注意,Stack均有列表和数组两种形式
IO流
转换流 : InputStreamReader OutputStreamWriter
打印流:PrintStream PrintWriter
数据流:DataInputStream DataOutputStream
对象流:ObjectinputStream ObjectOutputStream
随机存取文件流:RandomAccessFile
File类
- 访问文件名称
getName();getPath();getAbsolutePath();getAbsoluteFile();getParent();renameTo(File newName) - 文件检测
exists();canWrite();canRead();isFile();isDirectory();lastModify()时间戳;Length()字节数 - 文件操作
createNewFile();delete();mkDir();list();listFiles();
package com.learn.maven_wjh;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
public class IO_file {
public static void main(String[] args) {
File f = new File("D:\\Node.js\\新建文件夹\\新建文本文档.txt");
// 可以获取文件名或文件夹名称
System.out.println(f.getName());
System.out.println(f.getPath() + ", " + f.getAbsolutePath());
// 注意,只能识别到工程目录
File f1 = new File("src\\com\\learn");
System.out.println(f1.getAbsolutePath());
// createFile();
// createDirection();
File newFiles = new File(f.getParent());
}
public static void createFile() {
File oldFile = new File("E:\\IDEA_CODE\\src\\com\\learn\\maven_wjh\\file_test.txt");
// 如果文件不存在
if (!oldFile.exists()) {
try {
boolean new_judge = oldFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
if(oldFile.exists()){
System.out.println(oldFile.lastModified());
boolean delete_judge = oldFile.delete();
System.out.println(delete_judge);
}
}
public static void createDirection(){
File direction = new File("src\\com\\learn\\java_final");
File absolute_dir = new File(direction.getAbsolutePath());
boolean create_judge = absolute_dir.mkdirs();
// File parent_dir = new File(direction.getParent());
// System.out.println(Arrays.toString(parent_dir.list()));
File parent_file = new File(direction.getParent());
System.out.println(Arrays.toString(parent_file.listFiles()));
}
public static void walkFile(File file){
if (file.isFile()){
System.out.println(file.getAbsolutePath());
}else {
System.out.println(file.getAbsolutePath()+"is direction");
File[] files = file.listFiles();
if(files!=null&&files.length>0){
for (File file1 : files) {
walkFile(file1);
}
}
}
}
}
Stream
- 输入流:字节(8bit)InputStream 字符(16bit)Reader
- 输出流:字节(8bit)OutputStream 字符(16bbit)Writer
package com.learn.java_final;
import com.sun.org.apache.xml.internal.resolver.readers.ExtendedXMLCatalogReader;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Arrays;
// 注意一般使用InputSteam作为超类,FileInputStream作为子类进行文件读写,利用StringBuffer将read读取到的数字数组转为字符串进行拼接
public class tt_stream {
public static void main(String[] args) throws Exception {
String content;
// try (InputStream inputStream = new FileInputStream("D:\\Node.js\\新建文件夹\\新建文本文档.txt");) {
// content = readAsString(inputStream);
// }
// System.out.println(content);
out_read();
}
public static String readAsString(InputStream inputStream) throws Exception {
int n;
StringBuilder stringBuffer = new StringBuilder();
while ((n = inputStream.read()) != -1) {
stringBuffer.append((char) n);
}
return stringBuffer.toString();
}
public static void out_read() throws Exception {
// byte[] buffer = new byte[100];
byte[] bytes = {10, 4, 10, 30};
try (InputStream input = new ByteArrayInputStream(bytes)){
int n;
while ((n=input.read())!=-1){
System.out.print((char) n);
}
}
}
}
OutputStream
public static void writeSome() throws Exception {
try (OutputStream outputStream = new FileOutputStream("readme.txt")) {
// outputStream只能接受byte
outputStream.write("hello".getBytes(StandardCharsets.UTF_8));
// outputStream.flush() 将缓冲区的数据刷写到文件里
}
}
注意,对输入输出流进行filter时,所有的类型均向上转型为InputStream,是为了控制子类爆炸的情况出现
也可以自定义FilterInputStream
public class learnStream {
public static void main(String[] args) throws Exception {
byte[] data = "hello.world".getBytes(StandardCharsets.UTF_8);
try(CountInputStream inputStream = new CountInputStream(new ByteArrayInputStream(data))){
int n;
while ((n=inputStream.read())!=-1){
System.out.println((char) n);
}
System.out.println(inputStream.getBytesRead());
}
}
}
class CountInputStream extends FilterInputStream {
private int count = 0;
protected CountInputStream(InputStream in) {
super(in);
}
public int getBytesRead() {
return this.count;
}
public int read() throws IOException {
int n = in.read();
if (n != -1) {
this.count++;
}
return n;
}
public int read(byte[] b, int off, int len) throws IOException {
int n = in.read(b, off, len);
this.count += n;
return n;
}
}
字节流
注意:无对应文件时会报错,写入文件会全覆盖
public class copyFile {
public static void main(String[] args) throws FileNotFoundException {
String content = testFileReader("xx");
System.out.println(content);
testFileWriter(content,"readme.txt");
}
public static String testFileReader(String inPath){
StringBuilder stringBuilder = new StringBuilder();
try(FileReader fr = new FileReader(inPath);){
// 临时存储数据的字符数组
char[] c = new char[1000];
int len = 0;
while ((len=fr.read(c))!=-1){
stringBuilder.append(new String(c, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
return stringBuilder.toString();
}
public static void testFileWriter(String text,String outPath){
try {
FileWriter fw = new FileWriter(outPath);
fw.write(text);
fw.flush();
fw.close();
}catch (Exception e){
e.printStackTrace();;
}
}
public static void transferFile(String inPath,String outPath){
try{
FileReader fileReader = new FileReader(inPath);
FileWriter fileWriter = new FileWriter(outPath);
char[] chars = new char[100];
int len = 0;
while ((len = fileReader.read(c))!=-1){
fileWriter.write(c,0,len);
}
fileWriter.flush();
fileWriter.close();
fileReader.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
缓冲流
FileInputStream,FileOutPutStream,FileReader,FileWriter操作较慢,收到磁盘的制约
因此出现了对应的BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter
public class learnBufferFile {
public static void main(String[] args) {
try {
// testBufferedInputStream();
// testBufferedOutputStream();
copyFile();
} catch (Exception e) {
e.printStackTrace();
}
}
// 注意缓冲区大小,太小可能会出现乱码
public static void testBufferedInputStream() throws Exception {
InputStream in = new FileInputStream("D:\\\\Node.js\\\\新建文件夹\\\\新建文本文档.txt");
BufferedInputStream br = new BufferedInputStream(in);
byte[] bytes = new byte[100];
int len = 0;
StringBuilder stringBuilder = new StringBuilder();
while ((len = br.read(bytes)) != -1) {
stringBuilder.append(new String(bytes, 0, len));
}
System.out.println(stringBuilder.toString());
// 先开后关,栈型操作
br.close();
in.close();
}
public static void testBufferedOutputStream() throws IOException {
OutputStream out = new FileOutputStream("readme1.txt");
BufferedOutputStream bo = new BufferedOutputStream(out);
String s = "hello world";
bo.write(s.getBytes());
bo.flush();
bo.close();
out.close();
}
// 缓冲字符流来复制文件,注意缓冲区大小,太小可能会出现重复
public static void copyFile() throws IOException {
BufferedReader br = new BufferedReader(new FileReader("D:\\\\Node.js\\\\新建文件夹\\\\新建文本文档.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("readme1.txt"));
char[] c = new char[20];
int len = 0;
StringBuffer stringBuffer = new StringBuffer();
while ((len = br.read(c))!=-1){
stringBuffer.append(c);
}
bw.write(stringBuffer.toString());
bw.flush();
bw.close();
br.close();
}
}
转换流
InputStreamReader in = new InputStreamReader(new FileInputStream("xx"),"GBK");
OutStreamWriter os = new OutputStreamWriter(new FileOutputStream("xx"),"GBK");
通过转换流与BufferedReader与PrintWriter进行读写
public class LearnFile {
private static final File f = new File("." + File.separator + "testFile.txt");
private static final Scanner SCANNER = new Scanner(System.in);
public static void main(String[] args) throws IOException {
File target_file = createFile();
System.out.println("write first");
line_write(target_file);
System.out.println("then read");
line_read(target_file);
}
public static File createFile() throws IOException {
if (f.isFile()) {
System.out.println("file already exist,and to delete it");
} else {
System.out.println(f.createNewFile());
}
return f;
}
public static void line_read(File file1) {
try (InputStream inputStream = new FileInputStream(file1);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream,StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
) {
String line = null;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void line_write(File file2) {
try (OutputStream outputStream = new FileOutputStream(file2);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
PrintWriter printWriter = new PrintWriter(outputStreamWriter);
) {
while (true) {
String line_write = SCANNER.nextLine().trim();
if (line_write.trim().isEmpty()) {
System.out.println("end input");
break;
} else {
printWriter.println(line_write);
printWriter.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

浙公网安备 33010602011771号