Java注解、文件IO以及匿名函数

1. 注解

注解可以在类、方法、构造器、成员变量、参数等地方进行使用,其作用是让其他程序根据注解的信息来决定怎么执行程序。

1.1 自定义注解

自定义注解的形式如下:

public @interface 注解名{
  	public 属性类型 属性名() default 默认值;
}

特殊属性名:value。 如果注解中只有一个 value 属性,那么使用注解时, value 名称可以不写。

创建一个 Mytest1 的注解,注解源码如下:

public @interface Mytest1 {
    String a();
    boolean b() default true;
    String[] c();
}

使用注解时,可以在注解里面传值,如下,

@Mytest1(a="练习生", c={"唱", "跳", "rap", "篮球"})
public class Test1 {
    public static void main(String[] args) throws Exception {

    }
}

1.2 元注解

元注解即修饰注解的注解。

常用的元注解有 @Target 以及 @Retention

1.2.1 @Target

该注解声明被修饰的注解只能在哪些位置使用,使用注解时使用形式为 @Target(Element.TYPE) ,其中 TYPE 字段可变,可选值如下:

  1. TYPE :作用于类、接口
  2. FIELD :作用于成员变量
  3. METHOD :作用于成员方法
  4. PARAMETER :作用于方法参数
  5. CONSTRUCTOR :作用于构造器
  6. LOCAL_VARIABLE :作用于局部变量
  7. ANNOTATION_TYPE :作用于注解
  8. PACKAGE :作用于

该元注解可以作用于多个位置,如 @Target({Element.TYPE, Element.FIELD})

1.2.2 @Retention

该注解声明注解的保留周期,使用形式为 @Retention(RetentionPolicy.RUNTIME) ,其中 RUNTIME 字段可选值如下:

  1. SOURCE :只作用于源码阶段,字节码文件中不存在
  2. CALSS默认值,保留到字节码文件阶段,运行阶段不存在
  3. RUNTIME :一直保留到运行阶段,开发时常用

1.3 注解的解析

有时候加了一行注解后就能够实现很多的功能,看上去注解十分强大,其实不然,注解只是一个获取值的接口,注解本身并不能进行值的操作与解析功能,如果要对注解进行解析,那么需要在另一个文件中利用反射的方式来对其进行解析。

解析注解时,要注意的是,要解析谁上面的注解,就应该先拿到谁。比如要解析类上面的注解,就应该先获得加载类,获取 Class 对象,再通过 Class 对象解析上面的注解;要解析成员方法上面的注解,就应该先获取该成员方法上面的 Method 对象,再通过 Method 对象解析上面的注解。

Class,Method,Field,Constructor 都实现了 AnnotatedElement 接口,故都有解析注解的能力,解析注解主要使用的方法如下:

AnnotatedElement 提供的解析注解的方法
public Annotaion[] getDeclaredAnnotations()获取当前对象上面的注解
public T getDeclaredAnnotations(Class<T> annotationClass)获取指定的注解对象
public boolean isAnnotationPresent(Class<Annotation> annotationClass)判断当前对象上是否存在某个注解

比如我们可以对上面的 Test1 类的 @Mytest1 注解进行解析,解析如下

public class AnalyzeAnnotation {
    public static void main(String[] args) {
        Class c = Test1.class;

        // 判断是否存在Mytest1注解
        if(c.isAnnotationPresent(Mytest1.class)){
          	// 获取注解对象
            Mytest1 mytest1 = (Mytest1)c.getDeclaredAnnotation(Mytest1.class);
            System.out.println(mytest1.a());
            System.out.println(mytest1.b());
            System.out.println(Arrays.toString(mytest1.c()));

        }
    }
}

2. 文件IO

2.1 非流式文件类

在Java语言的 java.io 包中,由File类提供了描述文件和目录的操作与管理方法。但 File 类不是 InputStreamOutputStreamReaderWriter的子类,因为它不负责数据的输入输出,而专门用来管理磁盘文件与目录。

File 类的初始化如下:

// 根据路径来获取文件对象,这里也可以是一个文件夹
File file = new File("ikun.txt");

其常用的方法如下:

方法说明
boolean createNewFile创建一个新的文件 如果存在这样的文件,就不创建了
boolean mkdir创建文件夹 如果存在这样的文件夹,就不创建了 注意这个方法只能创建单层目录 如果创建多层目录得一层一层创建
boolean mkdirs创建文件夹,如果父文件夹不存在,会帮你创建出来 可以创建多层目录 当然也可以创建单层目录
boolean delete删除文件或者文件夹(文件夹是空文件夹才能删除!如果这个文件夹里面有文件,则不能删除! 要删除一个文件夹,该文件夹内不能包含文件或者文件夹)
boolean renameTo(File dest)把文件重命名为指定的文件路径(如果路径名相同,就会改名.如果路径名不同,就是改名并剪切)
boolean isDirectory()判断是否是目录
boolean isFile()判断是否是文件
boolean exists()判断是否存在
boolean canRead()判断是否可读
boolean canWrite()判断是否可写
boolean isHidden()判断是否隐藏
boolean isAbsolute()判断是否使用的是绝对路径
File getAbsolutePath()获取绝对路径
String getParent()返回此抽象路径名父目录的路径名字符串,如果此路径名没有指定父目录,则返回 null.
File getParentFile()返回此抽象路径名父目录的抽象路径名,如果此路径名没有指定父目录,则返回 null.
long getTotalSpace()返回此抽象路径名指定的分区大小。 返回总容量 单位字节
long getFreeSpace()返回此抽象路径名指定的分区中未分配的字节数。返回剩余容量 单位字节
String getName()获取名称
long length()获取长度.也就是文件大小,有多少字节数
long lastModified()获取最后一次的修改时间,单位为毫秒
String[] list()获取指定目录下的所有文件或者文件夹的名称,然后放入字符串数组
File[] listFiles()获取指定目录下的所有文件或者文件夹,然后放入File数组

2.2 流式文件类

流序列中的数据既可以是未经加工的原始二进制数据,也可以是经一定编码处理后符合某种格式规定的特定数据。因此Java中的流分为两种:

  1. 字节流:数据流中最小的数据单元是字节
  2. 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。

注意,以下的输入输出流都是相对内存而言的,即输入流是输入到内存,也就是读文件,输出流是从内存输出,也就是写文件。

2.2.1 字节输入流

字节输入流主要使用的是 FileInputStream 函数。

该函数的构造函数有两个形式,分别为 FileInputStream(String name), FileInputStream(File file) ,示例如下:

//直接通过文件地址初始化
FileInputStream fis = new ileInputStream("D:/test/test1.txt");

//通过File对象初始化
File file = new File("D:/test/test1.txt");
FileInputStream fis = new FileInputStream(file)

该读取的形式有两个主要如下:

read()读取一个字节(返回对应字节的ascii码值)
read(byte b[])根据字节缓冲数组的长度,进行读取(返回读取的字节数)

读取示例如下:

// 方式一
FileInputStream fis = new FileInputStream("ikun.txt");
while (true){
  	//read() 方法:从输入流对象中,一次读取一个字节(返回的是对应字节的ascii码值,int类型)
  	int hasRead = fis.read();

 	 //当读取到末尾,返回-1,代表文件读取结束
 	 if(hasRead == -1){
 		 break;
 	 }

  	System.out.print((char) hasRead);
  	//打印文件中字符的ascii值
	//转化为字符:KH96abcdefghijk
}

//最后一定要关闭资源
fis.close();


// 方式二
FileInputStream fis = new FileInputStream("D:/test/test1.txt");
//带缓冲字节数,根据字节缓冲数组的长度,进行读取
byte[] bytes = new byte[5];
//正确写法
int hasRead = 0;
while((hasRead = fis.read(bytes)) > 0){
  	//每次读取的内容
  	System.out.println(new String(bytes,0,hasRead));
}
fis.close();

2.2.2 字节输出流

字节输出流主要使用的是 FileOutputStream 函数。

其构造函数如下:

FileOutputStream(File file, boolean append)
FileOutputStream(String name)
FileOutputStream(String name, boolean append)

与输入流的构造函数类似,不过写入的文件不一定要存在,如果文件不存在,会自动创建一个空的文件;其后的 append 参数表示是否以追加的方式写入文件,默认值为 false 即覆盖,若设为 true 即为追加。

该写入的形式有两个主要如下:

write()将b.length个字节从指定字节数组写入此文件输出流中
write(byte b[], int off, int len)将指定字节数组中从偏移量off开始的len个字节写入此文件输出流

示例如下:

String string = "Ikun班,正在学习文件输出流,输出文件2";

//JDK1.7以后,只需将资源初始化放在try()里面就可以不用手动关闭流资源,推荐使用;
try(FileOutputStream fos =  new FileOutputStream("D:/test/test2.txt",true)){

    //将字符串转成字节数组,写入目标文件
    fos.write(string.getBytes());

    //手动刷新缓冲区
    fos.flush();
    
}catch (IOException e){
    e.printStackTrace();
}

2.2.3 字符输入流

Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流。

其构造函数如下:

FileReader(File file)
FileReader(String fileName)

读取如下:

read()按单个字符读取
read(char cbuf[])按字符数组长度读取

示例如下:

try(
  	//初始化字符读取流
  	FileReader frd =   new FileReader("ikun.txt");
){
  	//定义一个可变字符串对象
  	StringBuilder sbd = new StringBuilder();

  	//定义缓冲字符数组
  	char[] chars = new char[5];

  	int hasRead = 0; //读取到的字符长度
  	while((hasRead = frd.read(chars))>0){
    	sbd.append(new String(chars,0,hasRead));
  	}

  	//输出文件内容
  	System.out.println("文件全部内容:\n"+sbd.toString());
  	System.out.println("文件读取成功!");

} catch (FileNotFoundException e) {
  	e.printStackTrace();
} catch (IOException e) {
  	e.printStackTrace();
}

2.2.4 字符输出流

初始化如下:

FileWriter(String fileName)
FileWriter(File file, boolean append)
FileWriter(String fileName, boolean append)

其含义同字节输出流函数。

示例如下:

try( FileWriter fwr=  new FileWriter("D:/test/test2.txt")){

    //定义写入文件
    String string = "KH96,正在学习字符流写入文件";

    //直接写入目标文件
    fwr.write(string);

    //刷新缓冲区
    fwr.flush(); //一定要刷新缓冲区

    System.out.println("字符流写入成功!!!");
}catch (Exception e){
    e.printStackTrace();
}

3. 匿名

3.1 lambda表达式

java中的 lambda 表达式的一般格式如下:

(参数列表) -> {lambda表达式体}

lambda 表达式的情况可分为以下几种:

  1. 无参数,无返回值

    Runnable run = () -> {System.out.println("lambda表达式实现");};
    

    如果执行语句只有一行,可以省略”{}“:

    Runnable run = () -> System.out.println("lambda表达式实现");
    
  2. 有一个参数,无返回值

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

    参数类型可以省略,因为编译器会根据上下文环境推断出参数的类型,也叫做类型推断。这里指定了泛型为String。

    而且只有一个参数时,参数列表的”()“也可以省略。所以可以这么写:

    Consumer<String> consumer =  s -> System.out.println(s);
    
  3. 有多个参数,有返回值

    Comparator<Integer> comparator1 = (o1,o2) -> {
        System.out.println(o2);
        return Integer.compare(o1,o2);
    };
    
  4. 只有一条执行语句且有返回值

    Comparator<Integer> comparator1 = (o1,o2) -> {
        return Integer.compare(o1,o2);
    };
    

    如果有返回值时只有一条执行语句,那么”return“可以省略。

    Comparator<Integer> comparator1 = (o1,o2) -> Integer.compare(o1,o2);
    
  5. 将Lambda表达式作为参数传递

    Thread thread = new Thread(() -> {
        System.out.println("lambda表达式实现");
    });
    

    为了将Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该Lambda 表达式兼容的函数式接口的类型。比如上面代码中的Lambda表达式的作用就是创建了一个Runnale对象,而new Thread()的参数可以是一个Runnable对象。

3.2 方法引用

方法引用可以看做是lambda函数的语法糖,其引用与Lambda表达式对应如下:

类型方法引用Lambda表达式
Class::staticMethod(args) -> Class.staticMethod(args)
实例方法引用Instance::instanceMethod(args) -> Instance.instanceMethod(args)
对象方法引用Class::InstanceMethod(instance, args) -> Class.instanceMethod(args)
构造方法引用Class::new(args) -> new Class(args)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值