Desktop类和SystemTray类
使用JAXB2来实现对象与XML之间的映射
StAX
使用Compiler API
轻量级Http Server API
插入式注解处理API(Pluggable Annotation Processing API)
用Console开发控制台程序
对脚本语言的支持
Common Annotations
//如果数值在-128到127之前,对象会复用(享元设计模式)
System.out.println(i1 == i2);
-128到127会缓冲起来,节省内存。这是享元设计模式的应用,内部状态/外部状态(可变)
枚举
为什么要使用枚举?
比如,我们要使用1-7代表星期一到星期天,那么通常我们会想到的做法做,定义一个类,并且提供一些公有的静态常量,例如:
public class WeekDay {
public static final int SUN = 1;
public static final int MON = 2;
}
但是当我们使用的时候,有些人可能不想去理会这个细节,比如会直接传入1(可能他自己觉得1是代表星期一的),因此运行的时候就会出现一些意想不到的问题。
为了解决这个问题,java 5重新引入枚举,保证变量的值只能取指定值中的某一个,通过在代码编译的时候就可以知道传的值是否合法。
枚举的模拟:
/**
* 自己手动实现枚举类
* 1. 构造方法必须是私有的
* 2. 提供公有的静态成员变量代表枚举,并且通过匿名内部子类去生成对象
* 3. 对外提供的方法必须是抽象的
*/
public abstract class WeekDay {
public static WeekDay MON = new WeekDay(0) {
@Override
public WeekDay next() {
return SUN;
}
@Override
public String toString() {
return "SUN";
}
};
public static WeekDay SUN = new WeekDay(1) {
@Override
public WeekDay next() {
return MON;
}
@Override
public String toString() {
return "MON";
}
};
也可以直接调用无参数的构造,实际上也是先找到Constructor再调用Constructor的newInstance
String s = (String) Class.forName("java.lang.String").newInstance();
查看源码可以发现,Class的newInstance方法中有把Constructor缓存起来的。因为反射的使用会大大降低系统的性能,对于计算机来说比较耗时,而且也容易发生运行时异常,因此需要慎重使用。
Field对象
Field代表类中的一个成员变量
例如我们有一个测试的类Point
public class Point {
public int x;
private int y;
protected int z;
public Point(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
}
那么,我们可以利用发射机制去得到虚拟机中某个对象的成员变量,并且获取他们的值
Point point = new Point(1, 2, 3);
//通过反射拿到成员变量的值
Field x = point.getClass().getField("x");
System.out.println(x.get(point));
//如果是私有或者保护变量的话,不能拿到Field,会报找不到Field的错误
//Field y = point.getClass().getField("y");
//私有变量需要通过使用getDeclaredField去获取,如果要获取值的话,需要设置setAccessible为true
//这样的反射比较暴力
Field y = point.getClass().getDeclaredField("y");
y.setAccessible(true);
System.out.println(y.get(point));
例子,把对象中所有String类型的值中的a修改为*:
public class Reflecttest1 {
public static void main(String[] args) throws Exception {
Test test = new Test();
changeString(test);
System.out.println(test);
}
private static void changeString(Object obj) throws Exception {
for (Field field : obj.getClass().getFields()) {
public static void main(String[] args) throws Exception {
Method mainMethod = Class.forName("com.nan.test.T").getMethod("main", String[].class);
// mainMethod.invoke(null, new String[]{"123"});
mainMethod.invoke(null, new Object[]{new String[]{"123"}});
mainMethod.invoke(null, (Object) new String[]{"123"});
}
}
class T {
public static void main(String[] args) {
for (String s : args) {
System.out.println(s);
}
}
}
数组类型
具有相同的维度以及元素类型的数组,属于同一种Class类型。
例子:
int[] a1 = new int[3];
int[] a2 = new int[4];
int[][] a3 = new int[3][4];
String[] a4 = new String[3];
System.out.println(a1.getClass());
System.out.println(a2.getClass());
System.out.println(a3.getClass());
System.out.println(a4.getClass());
输出结果:
class [I
class [I
class [[I
class [Ljava.lang.String;
其中[代表是数组类型,I代表元素类型(int)
八种基本类型的数组不能转换成Object数组。因此下面的语句不合法:
Object[] objects = a1;
//因为基本类型的一维数组不能当成Object数组,因此只能当做是一整个数组对象
//因此打印出来的结果是数组的对象的值,而不是里面的内容
//那么,按照JDK1.5的可变长参数语法,只能解析成一个数组对象
System.out.println(Arrays.asList(a1));
//String类型可以转换成Object数组打印的是内容
System.out.println(Arrays.asList(a4));
数组
可以通过反射来对数组进行操作,因为是Object数组,因此不能确定整个数组是同一个类型,因此只能确定的是每一项的类型。
private static void printObj(Object o) {
//判断是不是数组类型
if (o.getClass().isArray()) {
//通过反射APIArray去遍历数组
int length = Array.getLength(o);
for (int i = 0; i < length; i++) {
Object item = Array.get(o, i);
System.out.println(item);
}
} else {
System.out.println(o);
}
}
equals与hashCode的联系与区别。
一般来说,两个都需要重写,而且在对象插入了hash集合以后,不要再修改这个对象与hash计算有关的数值了,因为这样会导致hash集合根据变化之后的hash值找不到这个对象了,对象不能被清理,从而造成内存泄漏。
HashSet set = new HashSet<>();
Point p0 = new Point(1,2,3);
Point p1 = new Point(1,2,3);
@Retention(RetentionPolicy.RUNTIME)//注解保留在哪个声明周期
@Target({ElementType.METHOD, ElementType.TYPE})//作用于什么元素身上
public @interface A {
}
注解作用的类:
@A
public class AnnotationTest {
}
获取注解(通过对AnnotationTest进行反射):
Class c = AnnotationTest.class;
//判断是否有对应的注解
if (c.isAnnotationPresent(A.class)) {
//获取注解
A a = c.getAnnotation(A.class);
}
元注解,用来给注解添加注解的注解
@Target(ElementType.METHOD) //声明注解可以作用在什么地方,特别注意TYPE是类、枚举、接口身上
@Retention(RetentionPolicy.SOURCE) //声明注解是在哪个声明周期的,分别是源代码,class文件,运行时
注意,多个属性的话需要用大括号括起来:
@Target({ElementType.METHOD, ElementType.TYPE})
注解的生命周期
通过反射获得注解之后,就可以随心去使用了:
Class c = AnnotationTest.class;
if (c.isAnnotationPresent(A.class)) {
A a = c.getAnnotation(A.class);
//获得stringAttr属性的值
System.out.println(a.stringAttr());
}
泛型
概念
集合,反射等等地方都使用到了泛型,免去了强制类型转换的不安全性问题,包括code阶段以及运行阶段。泛型是给编译器看的,让编译器拦截源程序中的非法输入,编译完以后就会去掉类型信息,保证程序的运行效率。对于参数化的泛型类型,getClass方法的返回值和原始类型完全一样。
所以编译完以后,跳过编译器,通过反射就可以向集合添加其他类型的数据,例子如下:
List list = new ArrayList<>();
//通过反射的方式取添加“非法类型”到集合当中
list.getClass().getMethod("add", Object.class).invoke(list, "abc");
System.out.println(list.get(0));
关于泛型的一些术语:
ArrayList 泛型类型
ArrayList中的E 类型变量或者类型参数
ArrayList 参数化的类型
ArrayList中的String 实际类型参数
ArrayList中的<>读作type of
用不用泛型,程序最终的运行结果都一样,用了有好处而已
参数化类型,不考虑类型参数的继承关系
例如,下面的这行代码是错误的,因为不考虑父子关系:
List
l.add(new Object());//这一局编译就报错
Object o = l.get(1);//返回值有泛型,但是我们可以转换为Object
for (Object obj : l) {
System.out.println(obj);
}
}
泛型的上下边界,可以用&实现多个接口的限定
//上边界
List extends Number> l1 = new ArrayList();
//下边界
List super Integer> l2 = new ArrayList
for (Map.Entry entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}
2.在for-each循环中遍历keys或values.如果只需要map中的键或者值,你可以通过keySet或values来实现遍历,而不是用entrySet。该方法比entrySet遍历在性能上稍好(快了10%),而且代码更加干净。
Map map = new HashMap();
//遍历map中的键
for (Integer key : map.keySet()) {
}
//遍历map中的值
for (Integer value : map.values()) {
}
3.使用迭代器,这种方法可以在迭代之中删除元素。
Map map = new HashMap();
例子:
private static T[] swap(T[] arr, int i, int j) {
T tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
return arr;
}
参数的类型推断(比较复杂)
类型参数的类型推断:编译器判断泛型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。根据调用泛型方法时实际传递参数类型或返回值的类型来推断,具体规则如下:
当某个类型变量值在整个参数列表中的所有参数和返回值中的一处被应用类,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:swap(new String[3],3,4) --> static void swap(E[] a,int i,int j)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:add(2,5) -->static T add (T a, T b)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到类不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:fill(new Integer[3],3.5f)-->static void fill(T[], T v)//Integer∩Float = Number ,它们都是Number的子类
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:int x = add(3,3.5f) -->static T add(T a,T b)
定义泛型的类型
方法级别(上面已经讲过)
泛型的类型(类):多个方法使用的是同一个类型
class A {
}
注意,类里面的静态方法不能含有对象的泛型。但是可以有一般的泛型静态方法。例子:
public class A {
//编译器报错,因为静态方法可以避开对象的创建嘛
public static void add(T t) {
Android中的Toast是一种简易的消息提示框,toast提示框不能被用户点击,toast会根据用户设置的显示时间后自动消失。
创建Toast
两个方法创建Toast
makeText(Context context, int resId, int duration)
参数:context是toast显示在
angular.identiy 描述: 返回它第一参数的函数. 此函数多用于函数是编程. 使用方法: angular.identity(value); 参数详解: Param Type Details value
*
to be returned. 返回值: 传入的value 实例代码:
<!DOCTYPE HTML>
Hierarchical Queries
If a table contains hierarchical data, then you can select rows in a hierarchical order using the hierarchical query clause:
hierarchical_query_clause::=
start with condi
初次接触到socket网络编程,也参考了网络上众前辈的文章。尝试自己也写了一下,记录下过程吧:
服务端:(接收客户端消息并把它们打印出来)
public class SocketServer {
private List<Socket> socketList = new ArrayList<Socket>();
public s