0%

Java基础查漏补缺

Java基础查漏补缺

日期相关

Date

java.util.Date

toString()输出形式是形如:Sat Jul 23 17:22:37 CST 2022的,包括到具体时间

两个常用构造器(其它已过时):
  1. 空参构造器,默认是当前日期时间

    1
    2
    Date d = new Date();
    System.out.println(d); // Sat Jul 23 17:22:37 CST 2022

  2. 带参构造器,传入一个长整型(时间戳)

    1
    2
    Date d = new Date(System.currentTimeMillis());
    System.out.println(d); // Sat Jul 23 17:23:59 CST 2022
    ##### 常用方法

  3. 日期转时间戳getTime()

    1
    2
    Date d = new Date();
    System.out.println(d.getTime()); // 1658568326143

java.sql.Date

继承自java.util.Date,toString()输出形式形如:2022-07-23,只有日期,没有具体时间

构造器

传入一个常整型参数(时间戳)

1
2
Date d = new Date(System.currentTimeMillis());
System.out.println(d); // 2022-07-23

常用方法

与java.util.date一样

1
2
Date d = new Date(System.currentTimeMillis());
System.out.println(d.getTime()); // 1658568525790

java.text.SimpleDateFormat

作用是将Date类格式化输出或将指定格式字符串转成Date类

两个构造器
  1. 空参构造器

    1
    2
    3
    SimpleDateFormat sdf = new SimpleDateFormat();
    Date date = new Date();
    System.out.println(sdf.format(date)); // 22-7-23 下午5:31
    默认格式是"22-7-23 下午5:31"

  2. 带参构造器,指定格式对Date进行格式化

    1
    2
    3
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    Date date = new Date();
    System.out.println(sdf.format(date)); // 2022-07-23 05:33:08

常用方法
  1. format

对日期进行格式化

1
2
3
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = new Date();
System.out.println(sdf.format(date)); // 2022-07-23 05:33:08

  1. parse

将符合格式的字符串解析成Date类,如果字符串不满足格式则抛出异常

1
2
3
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = sdf.parse("2022-07-25 03:22:01");
System.out.println(date); // Mon Jul 25 03:22:01 CST 2022

java.util.Calendar

Calendar是一个抽象类,主要用于完成日期字段之间相互操作的功能

获取实例的方法
  1. 调用静态方法Calendar._getInstance_(); 更常用,好记

    1
    Calendar calendar = Calendar.getInstance();

  2. 实例化具体子类new GregorianCalendar()

    1
    Calendar calendar = new GregorianCalendar();

常用方法
  1. get()

输入一个int型参数,实际使用中常用自带常量 :::success 获取月份时:一月是0,二月是1,以此类推 获取星期时:周日是1,周二是2,... :::

1
2
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.get(Calendar.DAY_OF_MONTH)); // 23(本月第23天)

  1. set()

修改对应的属于

1
2
3
4
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.get(Calendar.DAY_OF_WEEK)); // 7
calendar.set(Calendar.DAY_OF_WEEK, 2);
System.out.println(calendar.get(Calendar.DAY_OF_WEEK)); // 2

  1. add():添加指定类型数值

    1
    2
    3
    4
    Calendar calendar = Calendar.getInstance();
    System.out.println(calendar.get(Calendar.DAY_OF_YEAR)); // 204
    calendar.add(Calendar.DAY_OF_YEAR, 3);
    System.out.println(calendar.get(Calendar.DAY_OF_YEAR)); // 207

  2. getTime():获取当前时间对应的Date类

    1
    2
    3
    Calendar calendar = Calendar.getInstance();
    Date date = calendar.getTime();
    System.out.println(date); // Sun Jul 24 10:47:42 CST 2022

  3. setTime():设置当前日期

    1
    2
    3
    Calendar calendar = Calendar.getInstance();
    Date date = new Date();
    calendar.setTime(date);

jdk8新增的常用日期时间API(吸收的Joda-Time的)

本地日期LocalDate
实例化
  1. now():根据当前日期创建对象
  2. of(int year, int month, int dayOfMonth):根据指定日期创建对象
    1
    2
    LocalDate ld1 = LocalDate.now();
    LocalDate ld2 = LocalDate.of(2022, 1, 22);
常用方法
方法名 功能
getDayOfMonth()/getDayOfYear()/getDayOfWeek() 获取日期对应本月/年/周第几天
withDayOfMonth()/withDayOfYear() 设置当前是本月第几天或本年第几天
getMonth() 获取月份(如:JULY)
getMonthValue() 获取月份值(如:7)
getYear() 获取年份(如:2022)
plusDays()/plusWeeks()/plusMonths()/plusYears() 向当前日期增加指定天、周、月、年
minusDays()/minusWeeks()/minusMonths()/minusYears() 从当前日期减去指定天、周、月、年
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
LocalDate localDate = LocalDate.now();
System.out.println(localDate); // 2022-07-24
System.out.println(localDate.getDayOfMonth()); // 24
System.out.println(localDate.getDayOfYear()); // 205
System.out.println(localDate.getDayOfWeek()); // SUNDAY
System.out.println(localDate.getMonth()); // JULY
System.out.println(localDate.getMonthValue()); // 7
System.out.println(localDate.getYear()); // 2022
System.out.println(localDate.plusDays(1)); // 2022-07-25
System.out.println(localDate.plusWeeks(1)); // 2022-07-31
System.out.println(localDate.plusMonths(1)); // 2022-08-24
System.out.println(localDate.plusYears(1)); // 2023-07-24
System.out.println(localDate.minusDays(1)); // 2022-07-23
System.out.println(localDate.minusWeeks(1)); // 2022-07-17
System.out.println(localDate.minusMonths(1)); // 2022-06-24
System.out.println(localDate.minusYears(1)); // 2021-07-24
本地时间LocalTime
实例化

与LocalDate一样类似,其中LocalTime.of(int hour, int minute, int second, int nanoOfSecond)

1
2
3
4
LocalTime lt1 = LocalTime.now();
LocalTime lt2 = LocalTime.of(10, 25, 3, 100);
System.out.println(lt1); // 11:18:58.740
System.out.println(lt2); // 10:25:03.000000100

常用方法
方法名 功能
getHour()/getMinute()/getSecond() 获取时/分/秒
plusHours()/plusMinutes()/plusSeconds() 在当前时间基础上增加时/分/秒
minusHours()/minusMinutes()/minusSeconds() 在当前时间基础上减去时/分/秒
1
2
3
4
5
6
7
8
9
10
11
LocalTime lt1 = LocalTime.now();
System.out.println(lt1); // 11:22:38.998
System.out.println(lt1.getHour()); // 11
System.out.println(lt1.getMinute());// 22
System.out.println(lt1.getSecond());// 38
System.out.println(lt1.plusHours(1)); // 12:22:38.998
System.out.println(lt1.plusMinutes(1)); // 11:23:38.998
System.out.println(lt1.plusSeconds(1)); // 11:22:39.998
System.out.println(lt1.minusHours(1)); // 10:22:38.998
System.out.println(lt1.minusMinutes(1)); // 11:21:38.998
System.out.println(lt1.minusSeconds(1)); // 11:22:37.998
本地日期时间LocalDateTime

实际就是LocalDate与LocalTime的综合

瞬时Instant

类似于java.util.Date

实例化
  1. now():返回本初子午线上当前时间Instant类的对象,中国时间需要加8小时才行
  2. ofEpochMilli():返回在1970-01-01 00:00:00基础上加上指定毫秒数之后的Instant类对象
    1
    2
    3
    4
    Instant instant = Instant.now();
    System.out.println(instant); // 2022-07-24T03:34:25.634Z
    instant = Instant.ofEpochMilli(System.currentTimeMillis());
    System.out.println(instant); // 2022-07-24T03:34:25.692Z
常用方法
  1. atOffset(ZoneOffset offset):结合即时偏移
  2. toEpochMilli():返回对应时间戳
    1
    2
    3
    System.out.println(instant); // 2022-07-24T03:38:56.132Z
    System.out.println(instant.atOffset(ZoneOffset.ofHours(8))); // 2022-07-24T11:38:56.132+08:00
    System.out.println(instant.toEpochMilli()); // 1658633936132
日期时间格式化与解析java.time.format.DateTimeFormatter
实例化
  1. 预定义的标准格式:形如DateTimeFormatter._ISO_LOCAL_DATE_TIME;_
  2. 本地化相关的格式:形如DateTimeFormatter._ofLocalizedDate_(FormatStyle._LONG_);
  3. 自定义格式:形如DateTimeFormatter._ofPattern_("yyyy-MM-dd hh:mm:ss");
    1
    2
    3
    4
    5
    6
    7
    8
    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt); // 2022-07-24T11:47:07.662
    DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
    System.out.println(formatter.format(ldt)); // 2022-07-24T11:47:07.662
    DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);
    System.out.println(formatter1.format(ldt)); // 2022年7月24日
    DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
    System.out.println(formatter2.format(ldt)); // 2022-07-24 11:47:07
常用方法
  1. 格式化format
  2. 解析parse
    1
    2
    3
    4
    DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
    System.out.println(formatter2.format(ldt)); // 2022-07-24 11:47:07
    TemporalAccessor parse = formatter2.parse("2022-01-11 12:32:03");
    System.out.println(parse); // {SecondOfMinute=3, HourOfAmPm=0, MinuteOfHour=32, NanoOfSecond=0, MilliOfSecond=0, MicroOfSecond=0},ISO resolved to 2022-01-11
其它
  • ZoneId
  • ZonedDateTime
  • Clock
  • Duration
  • Period
  • TemporalAdjuster
  • TemporalAdjusters
image.png
image.png

枚举类

使用enum关键字来定义枚举类,则定义的类的父类实际上是Enum类

1
2
3
4
5
6
7
8
9
10
11
enum Season {
// 提供枚举类对象
SPRING("春天"), // 用逗号隔开
SUMMER("夏天"),
AUTUMN("秋天"),
WINTER("冬天");
String name;
Season(String name){
this.name = name;
}
}
也能实现接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
interface info{
void show();
}
enum Season implements info{
SPRING("春天"){
@Override
public void show() {
System.out.println("春天重写");
}
},
SUMMER("夏天"){
@Override
public void show() {
System.out.println("夏天重写");
}
},
AUTUMN("秋天"),
WINTER("冬天");
String name;
Season(String name){
this.name = name;
}

@Override
public void show() {
System.out.println("整体重写");
}
}
常用方法:

  1. values()方法:返回枚举类型的对象数组,可以方便遍历枚举类对象
  2. valueOf(String str)方法:
  3. toString()方法:返回当前枚举类对象常量名称,如SUMMER
    1
    2
    3
    System.out.println(Arrays.toString(Season.SPRING.values())); // SPRING
    System.out.println(Season.valueOf("SUMMER")); // 找到SUMMER()对象
    System.out.println(Season.SPRING); // [SPRING, SUMMER, AUTUMN, WINTER]

注解

jdk内置的三个基本注解

  1. @Override:限定重写父类方法,只能用于方法
  2. @Deprecated:用于表示所修饰的元素已过时
  3. @SuppressWarnings:抑制编译器警告

自定义注解

参照@SuppressWarnings定义

  1. 使用@interface声明
  2. 内部定义成员
    • 成员变量是以无参数方法的形式声明,方法名和返回值定义了成员的名字和类型
    • 如果只有一个参数成员,建议使用参数名为value
    • 可以在定义成员变量时通过default关键字指定初始值
    • 没有成员定义的注解称为标记,如@Override;有成员变量的称为元数据Annotation
      1
      2
      3
      4
      5
      6
      7
      public @interface MyAnno {
      String value() default "hello";
      }


      @MyAnno(value = "test")
      public class Test { }

jdk提供的4种元注解

元注解:用于修饰其它注解的

  • @Retention
    • 用于指定注解的生命周期,包含一个RetentionPolicy类型的成员变量,使用时必须为该value指定值
      • RetentionPolicy.SOURCE:在源文件有效,编译器直接丢弃这种策略的注解
      • RetentionPolicy.CLASS:在class文件中有效,当运行Java程序时,JVM不会保留注解。这是默认值
      • RetentionPolicy.RUNTIME:在运行时有效,当运行Java程序时,JVM会保留注解,程序可通过反射获取该注解
  • @Target
    • 用于指定被修饰的注解能用于修饰哪些程序元素
      • CONSTRUCTOR:用于修饰构造器
      • FIELD:用于修饰域
      • LOCAL_VARIABLE:用于修饰局部变量
      • METHOD:用于修饰方法
      • PACKAGE:用于修饰包
      • PARAMETER:用户修饰参数
      • TYPE:用于修饰类、接口(包括注解类型)或enum声明
  • @Documented
    • 用于指定该元注解修饰的注解类会被javadoc提取成文档,默认是不包括注解的(使用时必须将Rentention设为RUNTIME)
  • @Inherited
    • 被其修饰的注解将具有继承性,即一个类上如果使用的注解是被@Inherited修饰的,则这个类的子类也会自动被这个注解修饰
    • 实际使用较少

jdk8注解新特性

  • 可重复注解

jdk8之前的做法:@MyAnnos({@MyAnno("aaa"), @MyAnno("bbb")})

1
2
3
4
5
6
7
8
9
10
11
public @interface MyAnno {
String value() default "hello";
}
---
public @interface MyAnnos{
MyAnno[] value();
}
---
@MyAnnos({@MyAnno("aaa"), @MyAnno("bbb")})
Class A{
}
jdk8及之后的写法: 使用@Repeatable(xxx.class)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Retention(RetentionPolicy.RUNTIME) // 表示保留到运行时阶段,可以通过反射获取
@Target(ElementType.METHOD) // 表示只能用在方法上
@Repeatable(MyAnnos.class)
public @interface MyAnno {
String value() default "hello";
}
---
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CLASS)
public @interface MyAnnos{
MyAnno[] value();
}
---
@MyAnno("aaa")
@MyAnno("bbb")
public void test(){}

  • 类型注解

在@Target中

  • ElementType.TYPE_PARAMETER:可以修饰泛型
  • ElementType._TYPE_USE_:修饰泛型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Retention(RetentionPolicy.RUNTIME) // 表示保留到运行时阶段,可以通过反射获取
    @Target({ElementType.TYPE_PARAMETER})
    public @interface MyAnno {
    String value() default "hello";
    }
    ---
    class Test<@MyAnno T>{

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Retention(RetentionPolicy.RUNTIME) // 表示保留到运行时阶段,可以通过反射获取
    @Target({ElementType.TYPE_USE})
    public @interface MyAnno {
    String value() default "hello";
    }
    ---
    public void test() throws @MyAnno RuntimeException{
    List<@MyAnno String> list = new ArrayList<>();
    int n = (@MyAnno int) 10L;
    }

集合

Properties

Properties类是Hashtable的子类,用于处理属于文件 特点是:Proberties的key和value都是字符串类型 存取数据时使用:setProperty(String key, String value); getProperty(String key);

1
2
name=test
password=123
1
2
3
4
Properties p = new Properties();
p.load(new FileInputStream("test.properties"));
System.out.println(p.getProperty("name")); // test
System.out.println(p.getProperty("password")); // 123

泛型

自定义泛型结构:泛型类、泛型接口、泛型方法

如果子类继承带泛型的父类时,指明了泛型,则实例化子类对象时就不需要指明泛型;而如果没有指明,则子类依然是泛型类

  • 如果有泛型类有多个参数,则可将多个参数一起放在尖括号中,如:<E1, E2, E3>

  • 泛型类的构造器后面不用加<>,即:public Test()<>{}是错误的,应该直接public Test(){}

  • 泛型不同的引用,不能相互赋值,即:如下代码是错误的:

    1
    2
    3
    ArrayList<String> list1 = null;
    ArrayList<Integer> list2 = null;
    list1 = list2; // 编译错误

  • 静态方法中不能使用类的泛型,但可以使用自己的泛型

    1
    2
    public static void show(T t){} // 报错
    public static <E> void show(E e){} //不报错

  • 异常类不能使用泛型

  • 泛型不能使用new,即不能new E[10],但可以使用E[] es = (E[])new Object[10];

  • 泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系

    1
    public <E> List<E> copyFromArrayToList(E[] arr){}// 最前面的<E>是表示参数arr对应的E是泛型,而不是有E这个类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    interface Test<T1, T2, T3>{ // 泛型接口
    void test(T1 t);
    }

    class Order<T>{ // 泛型类
    String orderName;
    int orderId;
    T orderT;
    public Order(String name, int id, T t){ // 泛型方法
    this.orderName = name;
    this.orderId = id;
    this.orderT = t;
    }
    }

  • 通配符:?

    1
    2
    3
    4
    5
    List<String> l1 = null;
    List<Integer> l2 = null;
    List<?> l3 = null; // 对于List<?>就不能添加数据了,获取数据会得到Object类型数据
    l3 = l1;
    l3 = l2;

  • 有限制条件的通配符

    • 上限extends:指定类型必须是继承或本来是某个类(\(\le\)),如:<? extends Number>
    • 下限super:指定类型不能小于操作的类(\(\ge\)),如:<? super Number>

IO流

java.io.File类

表示文件和文件目录路径的抽象表示形式,与平台无关 File类的一个对象,代表一个文件或一个文件目录

实例化

  1. File file = new File(String path);根据路径(相对路径或绝对路径)实例化
    1. 为避免路径分隔符问题,java提供常量表示:File.separator
  2. File file = new File(String parent, String child); 如:File file = new File("D:\", "hello.txt");
  3. File file = new File(File parent, String child);

常用方法

方法名 描述
getAbsoluteFile() 获取绝对路径
getPath() 获取路径
getName() 获取名称
getParent() 获取上层目录路径,若无返回null
length() 获取文件长度(字节数)
lastModified() 获取最后一次修改时间,时间戳
list() 获取指定目录下所有文件或文件目录名称数组
listFiles() 获取指定目录下所有文件或文件目录File数组
boolean renameTo(File dest) 把文件重命名为指定的文件路径,需要源路径存在,目标路径不存在,成功后返回true
isDirectory() 是否是文件目录
isFile() 是否是文件
exists() 是否存在
canRead() 是否可读
canWrite() 是否可写
isHidden() 是否隐藏
createNewFile() 创建文件,若文件存在则不创建,返回false
mkdir() 创建文件目录,若存在则不创建
mkdirs() 创建文件目录,如果上层文件不存在则一起创建
delete() 删除文件或文件夹(文件夹中不能有文件或目录)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
File file = new File("test.iml");
System.out.println(file.getAbsoluteFile()); // D:\Code\JavaCode\test\test.iml
System.out.println(file.getPath()); // test.iml
System.out.println(file.getName()); // test.iml
System.out.println(file.getParent()); // null
System.out.println(file.length()); // 433
System.out.println(file.lastModified()); // 1657240974716
file = new File("src");
System.out.println(Arrays.toString(file.list())); // [MyAnno.java, test.java]
System.out.println(Arrays.toString(file.listFiles())); // [src\MyAnno.java, src\test.java]

File file = new File("test.iml");
System.out.println(file.isDirectory()); // false
System.out.println(file.isFile()); // true
System.out.println(file.exists()); // true
System.out.println(file.canRead()); // true
System.out.println(file.canWrite()); // true
System.out.println(file.isHidden()); // false

IO流

  • 四个抽象基类 | | 字节流 | 字符流 | | --- | --- | --- | | 输入流 | InputStream | Reader | | 输出流 | OutputStream | Writer |

  • IO流体系 | 分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 | | --- | --- | --- | --- | --- | | 抽象基类 | InputStream | OutputStream | Reader | Writer | | 访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter | | 访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter | | 访问管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter | | 访问字符串 | | | StringReader | StringWriter | | 缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter | | 转换流 | | | InputStreamReader | OutputStreamWriter | | 对象流 | ObjectInputStream | ObjectOutputStream | | | | 打印流 | | PrintStream | | PrintWriter | | 推回输入流 | PushbackInputStream | | PushbackReader | | | 特殊流 | DataInputStream | DataOutputStream | | |

FileReader

读入时,文件不存在会抛出异常

实例化
  1. Reader reader = new FileReader(String fileName);
  2. Reader reader = new FileReader(File file);
常用方法
  1. read():返回读入的一个字符(int型),如果达到文件末尾,返回-1
  2. read(char[] cbuf):返回每次读入到cbuf数组中的字符的个数,如果达到文件末尾则返回-1
  3. close():关闭流
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    Reader reader = null;
    try {
    File file = new File("hello.txt"); // 其它方法中是相对于当前Module, main方法中是相对于当前工程
    reader = new FileReader(file);
    // int data = reader.read();
    // while(data != -1){
    // System.out.print((char)data);
    // data = reader.read();
    //}
    // 简写 + 处理异常
    int data;
    while ((data = reader.read()) != -1) {
    System.out.print((char) data);
    }
    }catch (IOException e){
    e.printStackTrace();
    }finally {
    if(reader!=null){
    try {
    reader.close();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }
    }
    }

    // 输出:
    // HelloWorld!
    // 第二行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Reader reader = new FileReader("hello.txt");
    char[] cbuf = new char[5];
    int len = 0;
    while((len=reader.read(cbuf))!=-1){
    // for(int i=0;i<len;i++){
    // System.out.print(cbuf[i]);
    // }
    String s = new String(cbuf, 0, len);
    System.out.print(s);
    }
    reader.close();

FileWriter

写出时,文件不存在不会报错,会自动创建;如果存在则覆盖(或追加)

实例化

与FileReader类似

  1. Writer writer = new FileWriter(File file);
  2. Writer writer = new FileWriter(File file, boolean append);
  3. Writer writer = new FileWriter(String fileName);
  4. Writer writer = new FileWriter(String fileName, boolean append);
常用方法
  1. write(char[] cbuf):将字符数组写出
  2. write(String str):将字符串写出
  3. close():关闭流
    1
    2
    3
    4
    Writer writer = new FileWriter("new.txt");
    writer.write("hello world\n".toCharArray());
    writer.write("string");
    writer.close();

InputStream

与FileReader类似

实例化
  1. InputStream is = new FileInputStream(File file);
  2. InputStream is = new FileInputStream(String fileName);
常用方法
  1. read():返回读入的一个字节(int型),如果达到文件末尾,返回-1
  2. read(byte[] buf):返回每次读入到buf数组中的字符的个数,如果达到文件末尾则返回-1
  3. close():关闭流
    1
    2
    3
    4
    5
    6
    7
    8
    InputStream is = new FileInputStream("new.txt");
    int len;
    byte[] buf = new byte[5];
    while((len=is.read(buf))!=-1){
    String s = new String(buf, 0, len);
    System.out.print(s); // 如果有中文,就可能会存在乱码
    }
    is.close();

OutputStream

与FileWriter类似

实例化

与FileReader类似

  1. OutputStream os = new FileOutputStream(File file);
  2. OutputStream os= new FileOutputStream(File file, boolean append);
  3. OutputStream os= new FileOutputStream(String fileName);
  4. OutputStream os= new FileOutputStream(String fileName, boolean append);
常用方法
  1. write(byte[] buf):将字节数组写出
  2. close():关闭流

BufferedReader/BufferedWriter

与普通的FileReader、FileWriter类似,提高了读取和写出的速度,原因是内部提供了一个缓冲区 实例化:

  • Reader reader = new BufferedReader(new FileReader("hello.txt"));
  • Writer writer = new BufferedWriter(new FileWriter("hello.txt"));

使用与FileReader、FileWriter一样用 此外,多了flush()方法,用于刷新缓冲区(即立即将缓冲区数据读入/写出并清空)此外,还多了readLine()方法,用于读取一行 关闭时先关外层的流,再关内层的流;实际上关闭外层流时,会自动关闭内存流,所以内存流的关闭可省略

BufferedInputStream/BufferedInputStream

与普通的FileInputStream、FileOutputStream类似,提高了读取和写出的速度 实例化:

  • InputStream is = new BufferedInputStream(new FileInputStream("hello.txt"));
  • OutputStream os = new BufferedOutputStream(new FileOutputStream("hello.txt"));

使用与FileInputStream、FileOutputStream一样 此外,多了flush()方法,用于刷新缓冲区; 关闭时先关外层的流,再关内层的流;实际上关闭外层流时,会自动关闭内存流,所以内存流的关闭可省略

转换流InputStreamReader、OutputStreamWriter

InputStreamReader:将InputStream转换成Reader,实例化参数为InputStream,实例化后对象可以当作Reader用;

  • InputStreamReader isr = new InputStreamReader(is, "UTF-8");
  • 可指定字符编码集:ASCII、ISO8859-1、GB2312、GBK、Unicode、UTF-8

OutputStreamWriter:将OutputStream转换成Writer;实例化参数为OutputStream,实例化后对象可以当作Writer用

  • OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
  • 可指定字符编码集
    1
    2
    InputStream is = new FileInputStream("hello.txt");
    InputStreamReader isr = new InputStreamReader(is);

不常用流

标准流
  • System.in和System.out分别代表了系统标准的输入和输出设备
  • System.in类型是InputStream,默认从控制台输入
  • System.out类型是PrintStream,是OutputStream的子类,默认从控制台输出
  • System类的setIn(InputStream in)/setOut(PrintStream out)方式重新指定输入和输出的流
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    while(true){
    String data = br.readLine();
    if (data.equalsIgnoreCase("e") || data.equalsIgnoreCase("exit")) {
    System.out.println("程序结束");
    break;
    }
    System.out.println(data.toUpperCase());
    }
    br.close();
打印流
  • 实现将基本数据类型的数据格式转换为字符串输出
  • PrintStream和PrintWriter
  • 提供了一系列重载的print()和println()方法,用于多种类型数据的输出
数据流
  • DataInputStream和DataOutputStream
  • 方便操作Java的基本数据类型和String类型的数据
  • 实例化时传入参数为IO流
  • 方法
    • readBoolean()
    • readByte()
    • readChar()
    • readFloat()
    • readDouble()
    • readShort()
    • readLong()
    • readInt()
    • readUTF()
    • readFully()
    • 上述read换成write()即为DataOutputStream中的方法
对象流
  • ObjectInputStream和ObjectOutputStream
  • 用于存储和读取基本数据类型或对象的处理流。可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来
  • 序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
  • 反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
  • 不能序列化statictransient修饰的成员变量

对象序列化

  • 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许将这种二进制流持久保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流就可以恢复成原来的Java对象
  • 序列化的好处在于可将任何实现了Serializable接口的对象转换成字节数据,使其在保存和传输时可被还原
  • 序列化是RMI(Remote Method Invoke-远程方法调用)过程的参数和返回值都必须实现的机制,而RMI是JavaEE的基础。因此序列化机制是JavaEE平台的基础
  • 如果需要让某个对象支持序列化机制,则必须让对象所属类及其属于是可序列化的,为了某个类是可序列化的,该类必须实现如下两个接口之一,否则会抛出NotSerializableException异常
    • Serializable
      • 内部定义:static final long serialVersionUID = ???L;
      • 如果没有显示定义这个常量,则这个值是Java运行时环境根据类的内部细节自动生成。如类的实例变量做了修改,serialVersionUID可能发生变化 ,换句话说,可能会存在序列化后,修改类代码,然后无法反序列化回来的情况
      • 内部所有属性也需要是可序列化的(默认情况下,基本数据类型都是可序列化的)
    • Externalizable
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // 序列化
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hello.txt"));
      Person person = new Person("张三", 3);
      oos.writeObject(person);
      oos.flush();
      oos.close();

      // 反序列化
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hello.txt"));
      Person p = (Person) ois.readObject();
      System.out.println(p);
      ois.close();
随机存取文件流
  • RandomAccessFile类
  • 直接继承自java.lang.Object类,并实现了DataInput、DataOutput接口,表示这个类可读也可写
  • 支持“随机访问”方式,程序可以直接跳到文件的任意地方来读、写文件
    • 支持只访问文件的部分内容
    • 可以向已存在的文件后追加内容
  • 实例化:
    • RandomAccessFile raf = new RandomAccessFile(File file, String mode); // mode可选:"r", "rw", "rws"可读可写,同步文件内容和元数据的更新, "rwd":可读可写,同步文件内容更新
    • RandomAccessFile raf = new RandomAccessFile(String fileName, String mode);
  • RandomAccessFile对象包含一个记录指针,以标示当前读写处的位置,可以自由移动记录指针
    • getFilePointer():获取文件记录指针的当前位置
    • seek(long pos):将文件记录指针定位到pos位置
  • 作为输出流时,写出到的文件不存在则执行过程中自动创建,如果写出到的文件已存在则会对原文件内容进行覆盖(默认从头覆盖)
    1
    2
    3
    4
    5
    6
    7
    8
    RandomAccessFile raf = new RandomAccessFile("new.txt", "r");
    raf.seek(3);
    byte[] buf = new byte[5];
    int len;
    while((len=raf.read(buf))!=-1){
    System.out.print(new String(buf, 0, len)); // loWorld!
    }
    raf.close();
NIO中的Path、Paths、Files

NIO支持面向缓冲区(IO是面向流的)、基于通道的IO操作。NIO以更加高效的方式进行文件的读写操作

  • java.nio.channels.Channel
    • FileChannel:处理本地文件
    • SocketChannel:TCP网络编程的客户端的Channel
    • ServerSocketChannel:TCP网络编程的服务端的Channel
    • DatagramChannel:UDP网络编程中发送端和接收端的Channel
  • 早期的Java只提供了一个File类来访问文件系统,但File类功能有限,所提供的方法性能也不高。而且,大多数方法在出错时仅返回失败,并不会提供异常信息
  • NIO.2为了弥补这种不足,引入了Path接口,代表一个平台无关的平台路径,描述了目录结构中文件的位置。Path可以看成是File类的升级版本,实际引用的资源也可以不存在。
  • 以前IO操作:File file = new File("hello.txt");
  • NIO中:Path path = Paths.get("hello.txt");
  • NIO.2在java.nio.file包下提供了Files、Paths工具类,Files包含了大量静态的工具方法来操作文件;Paths则包含了两个返回Path的静态工厂方法
  • Paths类提供的静态get()方法用来获取Path对象:
    • static Path get(String first, String...more):用于将多个字符串串连成路径
    • static Path get(URI uri):返回指定uri对应的Path路径

Path常用方法

方法名 描述
toString() 返回调用Path对象的字符串表示
startsWith(String path) 判断是否以path路径开始
endsWith(String path) 判断是否以path路径结束
isAbsolute() 判断是否是绝对路径
Path getParent() 返回Path对象包含整个路径,不包含Path对象指定文件路径
Path getRoot() 返回调用Path对象的根路径
Path getFileName() 返回与调用Path对象关联的文件名
getNameCount() 返回Path根目录后面元素的数量
Path getName(int idx) 返回指定索引idx的路径名称
Path toAbsolute() 作为绝对路径返回调用Path对象
Path resolve(Path p) 合并两个路径,返回合并后的路径对应的Path对象
File toFile() 将Path转换成File类对象

Files类常用方法

方法名 描述
Path copy(Path src, Path dest, CopyOption... how) 文件复制
Path createDirectory(Path path, FileAttribute<?>... attr) 创建一个目录
Path createFile(Path path, FileAttribute<?>... attr) 创建一个文件
void delete(Path path) 删除一个文件或目录,如果不存在,则报错
deleteIfExists(Path path) 如果Path对应文件或目录如果存在则删除
Path move(Path src, Path desc, CopyOption... how) 文件移动
long size(Path path) 返回path指定文件的大小
exists(Path path, LinkOption... opts) 判断文件是否存在
isDirectory(Path path, LinkOption... opts) 判断是否是目录
isRegularFile(Path path, LinkOption... opts) 判断是否是文件
isHidden(Path path) 判断是否是隐藏文件
isReadable(Path path) 判断是否可读
isWriteable(Path path) 判断是否可写
notExists(Path path, LinkOption... opts) 判断文件是否不存在
SeekableByteChannel new ByteChannel(Path path, OpenOption... how) 获取与指定文件的连接,how指定打开方式
DirectoryStream newDirectoryStream(Path path) 打开path指定的目录
InputStream newInputStream(Path path, OpenOption... how) 获取InputStream对象
OutputStream newOutputStream(Path path, OpenOption... how) 获取OutputStream对象

网络编程

IP地址InetAddress

实例化

  1. InetAddress.getByName(String host);// host可以是IP地址也可以是域名
  2. InetAddress.getLocalHost(); 本地主机
    1
    2
    3
    4
    5
    6
    InetAddress ip = InetAddress.getByName("192.168.1.1");
    System.out.println(ip); // /192.168.1.1
    InetAddress ip2 = InetAddress.getByName("www.baidu.com");
    System.out.println(ip2); // www.baidu.com/180.101.49.12
    InetAddress localHost = InetAddress.getLocalHost();
    System.out.println(localHost); // LAPTOP-ABCDEFG/192.168.1.3

常用方法

  1. getHostName():获取主机名(域名或IP地址)
  2. getHostAddress():获取主机地址

Socket

实例化

  1. Socket socket = new Socket(InetAddress ip, int port);
  2. Socket socket = new Socket(String host, int port);

常用方法

  1. socket.getOutputStream(); 获取OutputStream流
  2. close():关闭socket
    1
    2
    3
    4
    5
    6
    InetAddress ip = InetAddress.getLocalHost();
    Socket socket = new Socket(ip, 8888);
    OutputStream os = socket.getOutputStream();
    os.write("hello".getBytes());
    os.close();
    socket.close();

ServerSocket

实例化

  1. ServerSocket serverSocket = new ServerSocket(int port);

常用方法

  1. Socket accept():阻塞serverSocket,返回从客户端接收的Socket
    1
    2
    3
    4
    5
    6
    7
    8
    9
    ServerSocket serverSocket = new ServerSocket(8888);
    Socket socket = serverSocket.accept();
    InputStream is = socket.getInputStream();
    // 直接写中文可能存在乱码,最好转换一下
    byte[] buf = new byte[10];
    int len;
    while((len = is.read(buf))!=-1){
    System.out.print(new String(buf, 0, len));
    }

DatagramSocket

实例化

  1. DatagramSocket socket = new DatagramSocket();
  2. DatagramSocket socket = new DatagramSocket(int port);
  3. DatagramSocket socket = new DatagramSocket(int port, InetAddress Iaddr);

常用方法

  1. send(DatagramPacket packet):发送数据报
    1. DatagramPacket packet = new DatagramPacket(byte[] buf, int length, InetAddress address, int port); // 实例化DatagramPacket
    2. DatagramPacket packet = new DatagramPacket(byte[] buf, int length);
    3. packet.getData():获取数据报中的内容(字节数组)
    4. packet.getLength():获取数据报中内容长度
  2. close():关闭socket
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 发送端
    DatagramSocket socket = new DatagramSocket();
    String s = "hello";
    DatagramPacket packet = new DatagramPacket(s.getBytes(), s.length(), InetAddress.getLocalHost(), 8888)
    socket.send(packet);
    socket.close();

    // 接收端
    DatagramSocket socket = new DatagramSocket(8888);
    byte[] buf = new byte[10];
    DatagramPacket packet = new DatagramPacket(buf, 0, buf.length);
    socket.receive(packet);
    System.out.println(new String(packet.getData(),0, packet.getLength()));
    socket.close();

URL编程

  • URL类

实例化:

  1. URL url = new URL(String spec);通过URL地址构造对象
  2. URL url = new URL(URL context, String spec);通过一个基URL和相对地址构造对象
  3. URL url = new URL(String protocol, String host, String file);
  4. URL url = new URL(String protocol, String host, int port, String file);

常用方法:

  1. getProtocol():获取协议名

  2. getHost():获取主机名

  3. getPort():获取端口号

  4. getPath():获取文件路径

  5. getFile():获取文件名

  6. getQuery():获取查询

    1
    2
    3
    4
    5
    6
    URL url = new URL("http://127.0.0.1/index.html?username=abc");
    System.out.println(url.getProtocol()); // http
    System.out.println(url.getHost()); // 127.0.0.1
    System.out.println(url.getPath()); // /index.html
    System.out.println(url.getFile()); // /index.html?username=abc
    System.out.println(url.getQuery()); // username=abc

  7. openConnection():开启一个URL连接,开启的连接可以通过connect真正连接上

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    URL url = new URL("https://cn.bing.com/th?id=OHR.DolbadarnCastle_ZH-CN5397592090_1920x1080.jpg");
    HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
    urlConnection.connect();
    InputStream is = urlConnection.getInputStream();
    System.out.println(url.getFile());
    FileOutputStream fos = new FileOutputStream("t.jpg");
    byte[] buffer = new byte[1024];
    int len;
    while((len=is.read(buffer))!=-1){
    fos.write(buffer, 0, len);
    }
    is.close();
    fos.close();
    urlConnection.disconnect();

反射

  • 反射(Reflection)是动态语言的关键,反射允许程序在执行期通过对应API获取任何类的内部信息,并能直接操作任意对象的内部属性和方法
  • 加载完类之后,在堆内存的方法区就会产生一个Class类型对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,可以通过这个对象看到类的结构。这个对象就像一面镜子,能通过它看到类的结构,所以称为“反射”
    • 正常方式:引入需要的"包类"名称->通过new实例化->取得实例化对象
    • 反射方式:实例化对象->getClass()方法->得到完整的"包类"名称
  • 反射相关的主要API
    • java.lang.Class:代表一个类
    • java.lang.reflect.Method:代表类的方法
    • java.lang.reflect.Field:代表类的成员变量
    • java.lang.reflect.Constructor:代表类的构造器
    • .....
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      Person p = new Person("Tom", 20);
      p.publicMethod();
      // 反射的形式做相同的事
      Class<Person> clazz = Person.class;
      Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);
      Person person = constructor.newInstance("Tom", 20);
      System.out.println(person); // Person{name='Tom', age=20}
      Field name = clazz.getDeclaredField("name");
      name.set(person, "Jerry"); // Person{name='Jerry', age=20}
      System.out.println(person);
      Method method = clazz.getDeclaredMethod("publicMethod");
      method.invoke(person); // 公有方法

      // 还通过反射能调用私有成员
      Field age = clazz.getDeclaredField("age");
      age.setAccessible(true); // 设置可访问
      age.set(person, 12);
      System.out.println(person); // Person{name='Jerry', age=12}
      Method privateMethod = clazz.getDeclaredMethod("privateMethod");
      privateMethod.setAccessible(true);
      privateMethod.invoke(person); // 私有方法
  • 反射与封装:
    • 封装性是一种建议型的,建议调用public不调用private,为了更好的代码安全性
    • 反射是一种功能型的,提供这种技术,在需要时可以使用,保证了Java的动态性特征
  • java.lang.Class类的理解:
    • 在程序运行时会加载类,而被加载到内存中的类称为运行时类,这样的运行时类就是Class的一个实例
    • 换句话说,Class的一个实例就对应着一个运行时类

获取Class实例的4种方式

  1. 调用运行时类的属于:Person.class

  2. 通过运行时类对象调用getClass():new Person().getClass()

  3. 调用Class的静态方法:Person.forName(String classPath);(使用较多)

  4. 通过类加载器加载

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Class<Person> clazz1 = Person.class;
    Person p = new Person();
    Class clazz2 = p.getClass();
    Class clazz3 = Class.forName("com.zephon.java.Person");
    System.out.println(clazz1==clazz2); // true
    System.out.println(clazz1==clazz3); // true
    ClassLoader classLoader = TestReflection.class.getClassLoader();
    Class clazz4 = classLoader.loadClass("com.zephon.java.Person");
    System.out.println(clazz1==clazz4); // true
    :::success 哪些类型可以有Class对象?

  5. class:外部类、成员(成员内部类、静态内部类)、局部内部类、匿名内部类

  6. interface:接口

  7. []:数组

  8. enum:枚举

  9. annotation:注解@interface

  10. primitive type:基本数据类型

  11. void :::

使用ClassLoader加载配置文件

1
2
3
4
5
6
7
8
        Properties properties = new Properties();
// FileInputStream fis = new FileInputStream("src\\test.properties"); // 默认识别为当前Module下
// properties.load(fis);
// fis.close();
ClassLoader classLoader = test.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("test.properties"); // 此时识别位置是src下
properties.load(is);
System.out.println(properties); // {password=123, name=名称}

反射作用

创建运行时类的对象

  1. newInstance():创建对应的运行时类的对象(调用的空参构造器,没有空参构造器或没有权限则抛出异常),所以通常在Java Bean中需要提供一个空参构造器(也方便父类默认调用super()使用)

获取运行时类的属性(开发中使用不多)

  1. Fields[] getFields():获取当前运行时类及其父类所有public属性
  2. Fields[] getDeclaredFields():获取当前运行时类所有属性(包括private,不包含父类属性)
  3. Field getField(String name):获取指定名的属性
  • Field:
    • int getModifiers():获取权限修饰符,返回整型,可用Modifier.toString(int m)转换回字符串
    • Class getType():获取数据类型
    • String getName():获取变量名
  1. Field getDeclaredField(String name)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    Class<Person> clazz = Person.class;
    Field[] fields = clazz.getFields();
    for (Field field : fields) {
    System.out.println(field); // public java.lang.String com.zephon.java.Person.name
    }
    Field[] fields1 = clazz.getDeclaredFields();
    for (Field field : fields1) {
    System.out.println(field);
    // public java.lang.String com.zephon.java.Person.name
    // private int com.zephon.java.Person.age
    }
    Field f = clazz.getField("name");
    int modifiers = f.getModifiers();
    System.out.println(Modifier.toString(modifiers)); // public
    Class type = f.getType();
    System.out.println(type); // class java.lang.String
    System.out.println(f.getName()); // name

获取运行时类的方法

  1. Method[] getMethods():获取当前运行时类及其父类所有public方法
  2. Method[] getDeclaredMethods():获取当前运行时类所有方法(包括private,不包含父类方法)
  3. Method getMethod(String name):获取指定名的方法
  • Method:
    • Annotation[] getAnnotations():获取所有注解
    • int getModifiers():获取权限修饰符
    • Class getReturnType():获取返回值类型
    • getName():获取方法名
    • Class[] getParameterTypes():获取所有参数的类型
    • Class[] getExceptionTypes():获取所有异常类型
  1. Method getDeclaredMethod(String name)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    Class<Person> clazz = Person.class;
    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
    System.out.println(method);
    }
    System.out.println("-------------------");
    Method[] methods1 = clazz.getMethods();
    for (Method method : methods1) {
    System.out.println(method);
    }
    Method m = clazz.getMethod("publicMethod");
    Annotation[] annotations = m.getAnnotations();
    System.out.println(Arrays.toString(annotations)); // [@java.lang.Deprecated()]
    int modifiers = m.getModifiers();
    System.out.println(Modifier.toString(modifiers)); // public
    Class<?> returnType = m.getReturnType();
    System.out.println(returnType); // void
    System.out.println(m.getName()); // publicMethod
    Class<?>[] parameterTypes = m.getParameterTypes();
    System.out.println(Arrays.toString(parameterTypes)); // []
    Class<?>[] exceptionTypes = m.getExceptionTypes();
    System.out.println(Arrays.toString(exceptionTypes)); // [class java.lang.Exception]

获取运行时构造器

  1. Constructor<?>[] getConstructors();获取当前运行时类中声明为public的构造器
  2. Constructor<?>[] getDeclaredConstructors();获取当前运行时类中所有构造器
  3. Constructor<?> getConstructor(Class ...a);获取指定参数类型的构造器
  4. Constructor<?> clazz.getDeclaredConstructor();
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Class<Person> clazz = Person.class;
    Constructor<?>[] constructors = clazz.getConstructors();
    for (Constructor<?> constructor : constructors) {
    System.out.println(constructor);
    // public com.zephon.java.Person()
    // public com.zephon.java.Person(java.lang.String,int)
    }
    Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
    for (Constructor<?> declaredConstructor : declaredConstructors) {
    System.out.println(declaredConstructor);
    // private com.zephon.java.Person(int)
    // public com.zephon.java.Person(java.lang.String,int)
    // public com.zephon.java.Person()
    }
    Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);
    System.out.println(constructor);

获取运行时类的父类及父类的泛型

  1. Class getSuperclass();获取运行时类的父类
  2. Type getGenericSuperclass();获取运行时类的带泛型的父类
  3. (ParameterizedType) genericSuperclass; ParameterizedType中的getActualTypeArguments()获取运行时类的带泛型的父类的泛型
    1
    2
    3
    4
    5
    6
    7
    8
    Class<Person> clazz = Person.class;
    Class<? super Person> superclass = clazz.getSuperclass();
    System.out.println(superclass); // com.zephon.java.Creature
    Type genericSuperclass = clazz.getGenericSuperclass();
    System.out.println(genericSuperclass); // com.zephon.java.Creature<java.lang.String>
    ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
    Type[] types = parameterizedType.getActualTypeArguments();
    System.out.println((Class)types[0].getName());

获取运行时类的接口、所在包、注解等

  1. Class[] getInterfaces();获取运行时类的接口
  2. Package getPackage();获取运行时类的包
  3. Annotation[] getAnnotations();获取运行时类的注解
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Class<Person> clazz = Person.class;
    Class<?>[] interfaces = clazz.getInterfaces();
    for (Class<?> anInterface : interfaces) {
    System.out.println(anInterface); // interface java.io.Serializable
    }
    Package aPackage = clazz.getPackage();
    System.out.println(aPackage); // package com.zephon.java
    Annotation[] annotations = clazz.getAnnotations();
    for (Annotation annotation : annotations) {
    System.out.println(annotation); // @com.zephon.java.MyAnno(value=test)
    }

调用运行时类中的指定属性、方法、构造器

  1. Field field = clazz.getDeclaredField(String name):获取指定属性
  • 如需要操作的是非public属性,则需要设置权限field.setAccessible(true);
  • Field:
    • field.setAccessible(true);设置权限可访问
    • field.set(Object obj, Object value);obj:指明设置哪个对象的属性 value:将此属性设置为多少
    • field.get(Object obj); obj:获取哪个对象的当前属性值
  1. Method method = clazz.getDeclaredMethod(String name);获取指定方法
  • 如需要操作的是非public方法,则需要设置权限field.setAccessible(true);
  • Method:
    • method.setAccessible(true);设置权限可访问
    • Object result = method.invoke(Object obj, Object ...args);方法调用,obj:方法的调用者;...args:方法的参数;result:方法返回值;如果obj是null或clazz则是调用静态方法
  1. Constructor<> constructor = clazz.getDeclaredConstructor(Class c);获取指定构造器,c:指明构造器参数列表类型
  • Constructor:
    • constructor .setAccessible(true);设置权限可访问
    • Object obj = constructor.newInstance(Object ...args);args:构造器参数;obj:返回的实例化对象

反射的应用:动态代理

  • 动态代理使用场景:

    • 调试
    • 远程方法调用
  • 相比静态代理优点:

    • 抽象角色中声明的所有方法都被转移到调用处理器的一个集中方法中处理,可以更加灵活和统一的处理众多方法
  • 静态代理:代理类和被代理类在编译期间就已经确定

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    interface ClothFactory{
    void produceCloth();
    }
    // 代理类
    class ProxyClothFactory implements ClothFactory{
    private ClothFactory factory;
    public ProxyClothFactory(ClothFactory factory){
    this.factory = factory;
    }
    @Override
    public void produceCloth() {
    System.out.println("代理类额外操作");
    this.factory.produceCloth();
    System.out.println("代理类做后续工作");
    }
    }
    class NikeClothFactory implements ClothFactory{

    @Override
    public void produceCloth() {
    System.out.println("被代理类的工作");
    }
    }

    // 测试
    // 创建被代理类对象
    NikeClothFactory nikeClothFactory = new NikeClothFactory();
    // 创建代理类对象
    ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nikeClothFactory);
    proxyClothFactory.produceCloth();

  • 动态代理:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    interface ClothFactory{
    void produceCloth();
    }
    class NikeClothFactory implements ClothFactory{

    @Override
    public void produceCloth() {
    System.out.println("被代理类的工作");
    }
    }

    class ProxyFactory{
    /**
    * 获取一个代理类对象
    * @param obj 被代理类对象
    * @return 代理类对象
    */
    public static Object getProxyInstance(Object obj){
    return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("代理类额外操作");
    Object res = method.invoke(obj, args); // 注:这里是obj,表示调用被代理类对象的方法
    System.out.println("代理类做后续工作");
    return res;
    }
    });
    }
    }
    // 测试
    NikeClothFactory nikeClothFactory = new NikeClothFactory();
    ClothFactory proxyInstance = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
    proxyInstance.produceCloth();

Java 8新特性

Lambda表达式

本质:作为函数式接口的实例 格式:

  • "->":lambda操作符 或 箭头操作符
  • "->"左边:lambda形参列表(其实就是接口中抽象方法的形参列表)
  • "->"右边:lambda体(其实就是重写的抽象方法的方法体)

使用:

  1. 无参无返回值

    1
    Runnable r = () -> {System.out.println("Hello");}

  2. 一个参数,无返回值

    1
    Consumer<String> con = (String s)-> {System.out.println(s);}

  3. 一个参数,无返回值,数据类型可以省略

    1
    Consumer<String> con = (s)-> {System.out.println(s);}

  4. 若只有一个参数,则参数小括号可省略

    1
    Consumer<String> con = s-> {System.out.println(s);}

  5. 若两个或两个以上参数,多条执行语句,并且有返回值

    1
    2
    3
    4
    Comparator<Integer> com = (x,y) -> {
    System.out.println("实现函数式接口");
    return x.compareTo(y);
    };

  6. 如果只有一条执行语句,return和大括号可省略

    1
    2
    Consumer<String> con = s-> System.out.println(s);
    Comparator<Integer> com = (x,y) -> Integer.compare(x, y);

函数式(Functional)接口

如果一个接口中只声明了一个抽象方法,则此接口就称为函数式接口:@FunctionalInterface 自定义函数式接口:

1
2
3
4
@FunctionalInterface
public interface MyInterface {
void method();
}

Java内置的四大核心函数式接口:

函数式接口 参数类型 返回类型 用途
Consumer 消费型接口 T void 对类型T的对象应用操作,包含方法: void accept(T t);
Supplier 供给型接口 T 返回类型为T的对象,包含方法:T get();
Function<T, R> 函数型接口 T R 对类型为T的对象应用操作,并返回结果,结果是R类型的对象。包含方法:R apply(T t);
Predicate 断定型接口 T boolean 确定类型为T的对象是否满足约束,并返回boolean值。包含方法:boolean test(T t);

案例:

1
2
3
4
5
6
7
8
9
10
11
12
public void test() throws Exception {
List<String> list = Arrays.asList("abc", "Abc", "weq");
List<String> res = filterString(list, s -> s.startsWith("A"));
System.out.println(res);
}
public List<String> filterString(List<String> list, Predicate<String> pre){
List<String> res = new ArrayList<>();
for (String s : list) {
if(pre.test(s)) res.add(s);
}
return res;
}

其它内置接口

函数式接口 参数类型 返回类型 用途
BiFunction<T,U,R> T,U R 对类型为T,U参数应用操作,返回R类型结果,包含方法:R apply(T t, U u);
UnaryOperator T T 对类型为T的对象进行一元运算,并返回T类型结果,包含方法:T apply(T t);
BinaryOperator T,T T 对类型为T的对象进行二元运算,并返回T类型的结果,包含方法:T apply(T t1, T t2);
BiConsumer<T, U> T,U void 对类型为T,U参数应用操作。包含方法为:void accept(T t, U u);
BiPredicate<T, U> T,U boolean 包含方法为:boolean test(T t,U u);
ToIntFunction

ToLongFunction ToDoubleFunction | T | int long double | 分别计算int、long、double值的函数 | | IntFunction LongFunction DoubleFunction | int long double | R | 参数分别为int、long、double类型的函数 |

方法引用与构造器引用

  • 当要传递给Lambda体的操作已经有实现的方法了,可以使用方法引用
  • 方法引用可以看作是Lambda表达式的深层次表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖
  • 要求:实现接口的抽象方法的参数列表和返回值类型必须与方法引用的方法的参数列表和返回值类型保持一致
  • 格式:使用操作符"::"将类(对象)与方法名分隔开来
  • 三种主要使用情况:
    • 对象::实例方法名
    • 类::静态方法名
    • 类::实例方法名

例:

1
2
3
4
Consumer<String> c = System.out::println;
// 等价于
Consumer<String> c = s -> System.out.println(s);
c.accept("hello");
1
2
3
Comparator<Integer> comp = Integer::compare;
// 等价于
Comparator<Integer> comp = (t1, t2)->Integer.compare(t1, t2);
1
2
3
Comparator<Integer> comp = Integer::compareTo;
// 等价于
Comparator<Integer> comp = (t1, t2)->t1.compareTo(t2);
构造器引用:
1
2
3
Supplier<Person> sup = Person::new;
// 等价于
Supplier<Person> sup = () -> new Person();
带参构造器:
1
2
3
4
5
Function<Integer, Person> func = Person::new;
func.apply(20);
// 等价于
Function<Integer, Person> func = age -> new Person(age);
func.apply(20);
1
2
3
BiFunction<String, Integer, Person> func = Person::new;
// 等价于
BiFunction<String, Integer, Person> func = (name, age) -> new Person(name, age);
数组引用:
1
2
3
Function<Integer, String[]> func = length-> new String[length];
// 等价于
Function<Integer, String[]> func = String[]::new;

Stream API

  • Stream是Java8中处理集合的关键抽象概念,可以指定对集合进行操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。
  • Stream 和Collection集合的区别:Collection是一种静态的内存数据结构,而Stream是有关计算的。前者主要面向内存,存储在内存中,后者主要面向CPU,通过CPU实现计算
  • Stream操作是延迟执行的,会等到需要结果时才执行
  • Stream 操作的三个步骤
    1. 创建Stream:一个数据源(如:集合、数组),获取一个流
    2. 中间操作:一个中间操作链,对数据源进行处理
    3. 终止操作:一旦执行终止操作,就执行中间操作链,并产生结果。之后不会再被使用

创建的Stream

  1. 通过集合
    1. Stream stream():返回一个顺序流
    2. Stream parallelStream():返回一个并行流
  2. 通过数组(Arrays的静态方法)
    1. Arrays.stream(T[] array);返回一个流
  3. 通过Stream类的静态方法of()
    1. Stream.of(T... values):返回一个流
  4. 创建无限流(使用较少)
    1. Stream.iterate(final T seed, final UnaryOperator f):迭代
      1. 例:Stream.iterate(0, t->t+2); // 遍历前n个偶数
    2. Stream.generate(Supplier s):生成

Stream的中间操作

1、筛选与切片
方法 描述
filter(Predicate p) 接收Lambda,从流中排除某些元素
distinct() 筛选,通过流所生成的元素的hashCode()和equals()去除重复元素
limit(long maxSize) 截断流,使其元素不超过给定数量
skip(long n) 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补
2、映射
方法 描述
map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream
mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream
mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream
flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流(会以类似解包的方式将嵌套的流解开)
3、排序
方法 描述
sorted() 产生一个新流,其中按自然顺序排序
sorted(Compartor com) 产生一个新流,其中按比较器顺序排序

Stream的终止操作

1、匹配与查找
方法 描述
allMatch(Predicate p) 检查是否匹配所有元素
anyMatch(Predicate p) 检查是否至少匹配一个元素
noneMatch(Predicate p) 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素总个数
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) 内部迭代(使用Collection接口需要用户去做迭代称为外部迭代)
2、归约
方法 描述
reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来得到一个值。返回T
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值,返回Optinal
1
2
3
Integer sum = list.stream().reduce(0, Integer::sum);
// 或者
list.stream().reduce((x1, x2)->x1+x2);
3、收集
方法 描述
collect(Collector c) 将流转换为其它形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

此外Collectors实例类提供了很多静态方法,可以方便地创建常见收集器实例

方法 返回类型 作用
toList List 将流中元素收集到List
toSet Set 将流中元素收集到Set
toCollection Collection 将流中元素收集到创建的集合
counting Long 计算流中元素的个数
summingInt Integer 对流中元素的整数属性求和
averagingInt Double 计算流中元素Integer属性的平均值
summarizingInt IntSummaryStatistics 收集流中Integer属性的统计值。如:平均值
joining String 连接流中每个字符串
maxBy Optional 根据比较器选择最大值
minBy Optional 根据比较器选择最小值
reducing 归约产生的类型 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
collectiongAndThen 转换函数返回的类型 包裹另一个收集器,对其结果转换函数
groupingBy Map<K, List> 根据某个属性值对流分组,属性为K,结果为V
partitioningBy Map<Boolean,List> 根据true/false进行分区

Optional

  • 空指针异常是导致java应用程序失败的最常见原因
  • Optinal类(java.util.Optional)是一个容器类,可以保存类型T的值,代表这个值存在。或仅仅保存null,表示这个值不存在。原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。

创建Optional类对象的方法:

  • Optional.of(T t):创建一个Optional实例,t必须非空
  • Optional.empty():创建一个空的Optional实例
  • Optional.ofNumber(T t):t可以为null

判断Optional容器中是否包含对象:

  • boolean isPresent():判断是否包含对象
  • void isPresent(Consumer<? super T> consumer):如果有值,就执行consumer接口的实现代码,并且该值会作为参数传给它

获取Optional容器的对象:

  • T get():如果调用对象包含值,则返回,否则抛出异常
  • T orElse(T other):如果有值则返回,否则返回指定的other
  • T orElseGet(Supplier<? extends T> other):如果有值则返回,否则返回Supplier接口实现提供的对象
  • T orElseThrow(Supplier<? extends X> exceptionSupplier):如果有值则返回,否则抛出由Supplier接口实现提供的异常