I/O流

Socket通信

简单响应HTTP请求

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) {
ServerSocket serverSocket = new ServerSocket(8848);
while(true) {
Socket client = serverSocket.accept();
System.out.println("收到請求");
OutputStream recevieStream = client.getOutputStream();
String body = "<h1>Hello,world!</h1>";
recevieStream.write(("HTTP/1.1 200 OK\nContent-Type: text/html; charset-utf-8\n\n"+body).getBytes());
recevieStream.flush();
recevieStream.close();
client.close();
}
}

这样,在浏览器地址栏打开localhost:8848就会呈现出Hello,world!

反射——框架的灵魂

反射是动态获取类或对象信息以及动态调用对象方法的机制
通过反射可以获取并调用任意一个类的所有属性和方法。
反射之所以被称为框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力。

Class类

代表类的实体,在运行的Java应用程序中表示类和接口

获取Class对象

  1. 知道具体类的情况下可以使用:
    1
    Class<?> c = Test.class;
  2. 通过Class.forName()传入类的路径(全限定名)获取:
    1
    Class<?> c = Class.forName("package.Test");
  3. 通过对象实例.getClass()获取:
    1
    2
    Test t = new Test();
    Class<?> c = t.getClass();
  4. 通过类加载器xxxClassLoader.loadClass()传入类路径获取:
    1
    class c = ClassLoader.LoadClass("package.Test");
    通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一些列步骤,静态块和静态对象不会得到执行
  • 主要方法
    方法 用途
    asSubclass(Class<U> clazz) 把传递的类的对象转换成代表其子类的对象
    Cast 把对象转换成代表类或是接口的对象
    getClassLoader() 获得类的加载器
    getClasses() 返回一个数组,数组中包含该类中所有公共类和接口类的对象
    getDeclaredClasses() 返回一个数组,数组中包含该类中所有类和接口类的对象
    forName(String className) 根据类名返回类的对象
    getName() 获得类的完整路径名字
    newInstance() 创建类的实例
    getPackage() 获得类的包
    getSimpleName() 获得类的名字
    getSuperclass() 获得当前类继承的父类的名字
    getInterfaces() 获得当前类实现的类或是接口
    方法 用途
    getField(String name) 获得某个公有的属性对象
    getFields() 获得所有公有的属性对象
    getDeclaredField(String name) 获得某个属性对象
    getDeclaredFields() 获得所有属性对象
    方法 用途
    getAnnotation(Class<A> annotationClass) 返回该类中与参数类型匹配的公有注解对象
    getAnnotations() 返回该类所有的公有注解对象
    getDeclaredAnnotation(Class<A> annotationClass) 返回该类中与参数类型匹配的所有注解对象
    getDeclaredAnnotations() 返回该类所有的注解对象
    方法 用途
    getConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的公有构造方法
    getConstructors() 获得该类的所有公有构造方法
    getDeclaredConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的构造方法
    getDeclaredConstructors() 获得该类所有构造方法
    方法 用途
    getMethod(String name, Class...<?> parameterTypes) 获得该类某个公有的方法
    getMethods() 获得该类所有公有的方法
    getDeclaredMethod(String name, Class...<?> parameterTypes) 获得该类某个方法
    getDeclaredMethods() 获得该类所有方法
    方法 用途
    isAnnotation() 如果是注解类型则返回true
    isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果是指定类型注解类型则返回true
    isAnonymousClass() 如果是匿名类则返回true
    isArray() 如果是一个数组类则返回true
    isEnum() 如果是枚举类则返回true
    isInstance(Object obj) 如果obj是该类的实例则返回true
    isInterface() 如果是接口类则返回true
    isLocalClass() 如果是局部类则返回true
    isMemberClass() 如果是内部类则返回true

Field类

Field 代表类的成员变量(成员变量也称为类的属性)C#中称其为字段

  • 主要方法
    方法 用途
    equals(Object obj) 属性与obj相等则返回true
    get(Object obj) 获得obj中对应的属性值
    set(Object obj, Object value) 设置obj中对应属性值

Method类

Method 代表类的方法

  • 主要方法
    方法 用途
    invoke(Object obj, Object… args) 传递object对象及参数调用该对象对应的方法

Constructor类

Constructor 代表类的构造方法

  • 主要方法
    方法 用途
    newInstance(Object… initargs) 根据传递的参数创建类的对象

反射机制优缺点

  • 优点: 可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利
  • 缺点: 让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。

静态代理、动态代理

请移步Java版设计模式-代理模式

参考

泛型

泛型是通过 参数化类型 提高代码重用性,并在编译期强制进行类型检查的机制。

泛型分类

  • 泛型类
  • 泛型接口
  • 泛型方法

泛型通配符

  • 无边界通配符 <?>
    • 无界通配符可以适配任何引用类型,看起来与原生类型等价,但与原生类型还是有区别,使用无界通配符则表明在使用泛型<? extends Object>,因为Object是顶层类,所以看起来和<?>一样,但它特指可以使用哪些泛型对象类。
  • 固定上边界通配符 <? extends T>
    • 当泛型T给定形如 A类型到A类型任何子类 的限制域,可以匹配任何在此限制域中的类型,此种表示叫上边界通配符。
  • 固定下边界通配符 <? super T>
    • 当泛型T给定形如 A类型到A类型任何父类 的限制域,可以匹配任何在此限制域中的类型,此种表示叫下边界通配符。

注解(Annotation)

注解是用于对程序元素设置元数据,从而简化、改进程序开发的引用数据类型。

内置注解

  • 常用注解
    • @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
    • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
    • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
      • @SuppressWarnings("unused") - 抑制编译器发生 变量未使用 警告
      • @SuppressWarnings("rawtypes") - 抑制编译器发生 实例化对象时未遵循泛型格式 警告
      • @SuppressWarnings("all") - 抑制编译器发生 所有 警告
  • 从 Java 7 开始,额外添加了 3 个注解:
    • @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
    • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
    • @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

自定义注解

  • 使用@interface来表示一个注解
    只能声明成员方法,并且只能是抽象或public方法
    可以使用default设置默认值
    1
    2
    3
    4
    public @interface MyAnnotation {
    int attr1();
    String attr2() default "attr2的默认值";
    }
    1
    2
    3
    @MyAnnotation(attr1 = 0,attr2 = "改变了attr2的默认值")
    public class Test {
    }

元注解

元注解是用于描述注解的注解。 只能描述注解(@interface) ,不能描述类或方法。

  • @Retention - 解释说明注解的的生命周期

    属性 生命周期
    @Retention(RetentionPolicy.SOURCE) 注解在 源码阶段(.java文件) 保留,在编译时丢弃
    @Retention(RetentionPolicy.CLASS) 注解在 编译时(.class文件) 保留(不会被加载到JVM中)
    @Retention(RetentionPolicy.RUNTIME) 注解在 运行时 保留(会被加载到JVM中)
  • @Documented - 被描述的注解可在文档中体现
    如果使用@Documented标注了,在生成javadoc的时候就会把@Documented注解给显示出来。
    @Documented注解只是用来做标识,没什么实际作用,了解就好。

  • @Target - 限定注解的运用场景

    属性 运用场景
    @Target(ElementType.ANNOTATION_TYPE) 注解
    @Target(ElementType.CONSTRUCTOR) 构造方法
    @Target(ElementType.FIELD) 属性
    @Target(ElementType.LOCAL_VARIABLE) 局部变量
    @Target(ElementType.METHOD) 方法
    @Target(ElementType.PACKAGE)
    @Target(ElementType.PARAMETER) 方法的参数
    @Target(ElementType.TYPE) 类、接口、枚举
  • @Repeatable - 标记的注解可多次应用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Retention(RetentionPolicy.RUNTIME)
    @Repeatable(MyAnnotations.class)
    public @interface MyAnnotation {
    String value();
    }
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotations {
    MyAnnotation[] value();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @MyAnnotation("value1")
    @MyAnnotation("value2")
    @MyAnnotation("value3")
    public class Test {
    public static void main(String[] args) {
    for (MyAnnotation m : Test.class.getAnnotation(MyAnnotations.class).value()) {
    System.out.println(m.value());
    }
    //结果是value1 value2 value3
    }
    }
  • @Inherited - 允许子类继承父类的注解
    在注解上使用@Inherited 表示该注解会被子类继承,仅针对 类/成员属性 ,方法并不受此注释的影响。

Java中的正则表达式

测试是否匹配的三种方式

1
2
3
4
5
6
7
8
9
10
String regex = "\\d+";
String str = "123456";
//第一种方式
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);
System.out.println(m.matches());
//第二种方式
System.out.println(Pattern.matches(regex, str));
//第三种方式
System.out.println(str.matches(regex));

捕获组

1
2
3
4
5
6
7
String regex = "(?<num>\\d+)";
String str = "adad123456adas";
//第一种方式
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);
m.find(); //必要,必须调用后m.start() m.end() m.group()才会有效
System.out.println(m.group("num"));

dom4j操作xml文件

dom4j.jar官网 Github Releases


DOM4J是dom4j.org出品的一个开源XML解析包。DOM4J应用于Java平台,采用了Java集合框架并完全支持DOM,SAX 和JAXP。

DOM4J 最大的特色是使用大量的接口。它的主要接口都在org.dom4j里面定义:

接口 描述
Attribute 定义了 XML 的属性。
Branch 指能够包含子节点的节点。如XML元素(Element)和文档(Docuemnts)定义了一个公共的行为
CDATA 定义了 XML CDATA 区域
CharacterData 是一个标识接口,标识基于字符的节点。如CDATA,Comment, Text.
Comment 定义了 XML 注释的行为
Document 定义了XML 文档
DocumentType 定义 XML DOCTYPE 声明
Element 定义XML 元素
ElementHandler 定义了Element 对象的处理器
ElementPath 被 ElementHandler 使用,用于取得当前正在处理的路径层次信息
Entity 定义 XML entity
Node 为dom4j中所有的XML节点定义了多态行为
NodeFilter 定义了在dom4j 节点中产生的一个滤镜或谓词的行为(predicate)
ProcessingInstruction 定义 XML 处理指令
Text 定义 XML 文本节点
Visitor 用于实现 Visitor模式
XPath 在分析一个字符串后会提供一个 XPath 表达式

XML文档操作

  • 读取XML文档获取根节点
    读写XML文档主要依赖于org.dom4j.io包,有DOMReader和SAXReader两种方式。因为利用了相同的接口,它们的调用方式是一样的。
    1
    2
    SAXReader saxReader = new SAXReader();
    Document document = saxReader.read(new File("src/test.xml"));
    根节点是xml分析的开始,任何xml分析工作都需要从根开始
    1
    Element rootElement = document.getRootElement();

    节点操作

    读取节点

    通过xpath查找指定的节点

    采用xpath查找需要引入jaxen-xx-xx.jar,否则会报java.lang.NoClassDefFoundError:org/jaxen/JaxenException异常。
    jaxen.jar下载地址

添加节点

删除节点

CDATA节点

属性操作

参考

Properties

  • 通过流加载.properties文件
    1
    2
    Properties config = new Properties();
    config.load(new FileInputStream("src/test.properties"));
    关于InputStream:
    如果是在静态方法中可以使用Thread.currentThread().getContextClassLoader().getResourceAsStream("test.properties")
    如果是一个成员方法可以使用getClass().getResourceAsStream("test.properties")
    使用缓冲输入流也是个好的选择new BufferedInputStream (new FileInputStream("src/test.properties"));
  • 读取
    1
    String value = config.getProperty("key");
  • 其他方法
    • setProperty(String key, String value) ,调用 Hashtable 的方法 put 。他通过调用基类的put方法来设置键值对
    • store(OutputStream out, String comments),以适合使用 load 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。与 load 方法相反,该方法将键值对写入到指定的文件中去。
      comments代表注释,可以写个空字符串
    • clear()清除所有装载的键值对。该方法在基类中提供。

线程与线程池

Lambda表达式

Java Lambda表达式的一个重要用法是简化某些匿名内部类(Anonymous Classes)的写法。
实际上Lambda表达式并不仅仅是匿名内部类的语法糖,JVM内部是通过invokedynamic指令来实现Lambda表达式的,书写Lambda表达式不会产生新的类。

this

既然Lambda表达式不是内部类的简写,那么Lambda内部的this引用也就跟内部类对象 没有任何关系 了。在Lambda表达式中this的意义跟在表达式外部完全一样。
也就是说, Lambda内部的this指的是Lambda外部的类的对象

参考