forked from Seanforfun/Algorithm-and-Leetcode
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathJavaConclusion.txt
executable file
·236 lines (194 loc) · 10.1 KB
/
JavaConclusion.txt
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
---------------------------------Exception and Error--------------------------------------------
Throwable 是所有 Java 程序中错误处理的父类 ,有两种子类: Error 和 Exception 。
Error :表示由 JVM 所侦测到的无法预期的错误,由于这是属于 JVM 层次的严重错误 ,导致 JVM 无法继续执行,因此,这是不可捕捉到的,无法采取任何恢复的操作,顶多只能显示错误信息。
Exception :表示可恢复的例外,这是可捕捉到的。
Java 提供了两类主要的异常 :runtime exception 和 checked exception 。 checked 异常也就是我们经常遇到的 IO 异常,以及 SQL 异常都是这种异常。 对于这种异常, JAVA 编译器强制要求我们必需对出现的这些异常进行 catch 。所以,面对这种异常不管我们是否愿意,只能自己去写一大堆 catch 块去处理可能的异常。
但是另外一种异常: runtime exception ,也称运行时异常,我们可以不处理。当出现这样的异常时,总是由虚拟机 接管。比如:我们从来没有人去处理过 NullPointerException 异常,它就是运行时异常,并且这种异常还是最常见的异常之一。
---------------------------------Exception-----------------------------------------------------------
*****************Throw and Throws*************************************
Throw:程序员自己抛出异常的语句:
throw new RuntimeException();
Throws:声明方法需要抛出异常的定义:
public void test() throws Exception{
}
*****************Exception********************************************
Throwable
/ \
Error Exception
/ \
checked Runtime
1.Error 和 Exception 都继承自Throwable类,都可以通过throw语句向上抛出。
2.我个人认为,异常分为两类,可预计的和不可预计的两种,我将不可预计的异常统称为RuntimeException。所有RuntimeException均被认为是程序员自己造成的异常,可以通过统一异常处理。
3.什么时候处理异常,什么时候抛出异常:
1.处理当前可以解决的异常。
2.抛出无法解决的异常。
4.finally:
1.finally语句不一定会执行到,有两种情况:
->try语句没有执行到,在之前程序就返回了。
->在try中出现了System.exit(0),这样虚拟机就被关闭了,finally也不会被执行。
2.当try和catch中有return时,finally仍然会执行;
3.finally是在return语句执行之后,返回之前执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,不管finally中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),所以函数返回值是在finally执行前就已经确定了;
4.finally中如果包含return,那么程序将在这里返回,而不是try或catch中的return返回,返回值就不是try或catch中保存的返回值了。
---------------------------------Proxy---------------------------------------------------------------
代理是一种面向切面编程的实现AOP,我们可以通过代理对方法进行增强。分为静态代理和动态代理,而动态代理又分为API(java.lang.reflect)中的Proxy和Cglib,后两种用于Spring。
1.静态代理:
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.
1.定义一个要实现方法的接口:
public interface IUserDao {
void save();
}
2.定义一个目标对象:
public class UserDao implements IUserDao {
public void save() {
System.out.println("----已经保存数据!----");
}
}
3.定义一个代理对象,在构造函数中接收第二步生成的目标对象,并在代理类中调用目标对象实现方法,并且实现增强。
public class UserDaoProxy implements IUserDao{
//接收保存目标对象
private IUserDao target;
public UserDaoProxy(IUserDao target){
this.target=target;
}
public void save() {
System.out.println("开始事务...");
target.save();//执行目标对象的方法
System.out.println("提交事务...");
}
}
4.在调用方法时不直接调用对象,而是通过静态代理类调用方法。
public class App {
public static void main(String[] args) {
//目标对象
UserDao target = new UserDao();
//代理对象,把目标对象传给代理对象,建立代理关系
UserDaoProxy proxy = new UserDaoProxy(target);
proxy.save();//执行的是代理的方法
}
}
总结:
1.可以实现方法的横向增强。
2.要维护很多实现类。
2.动态代理:
1.java.lang.reflect实现:
这种方法通过调用java.lang.reflect中的Proxy方法实现:
1.定义一个接口:
public interface ProxyTestInterface {
public void test() throws Exception;
}
2.定义该接口的实现类:
public class ProxyTestImpl implements ProxyTestInterface {
@Override
public void test() throws Exception {
System.out.println("test");
}
}
3.通过Proxy中的newProxyInstance方法创建一个proxy对象:
public class ProxyFactory {
private static ProxyTestInterface instance = new ProxyTestImpl(); //在invoke方法中需要一个实现类对象,所以通过新建,或是接收参数的方法传入一个实现类对象。
@SuppressWarnings("rawtypes")
public static ProxyTestInterface getProxy(Class iterface){
// newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
// loader:生成当前代理的类的加载器。
// interfaces:生成的代理类的接口的数组。
// h:对代理类调用的Handler
return (ProxyTestInterface)Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(), new Class[]{iterface}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().contains("test")){ //选择Jointpoint
System.out.println("before advice"); //对代理实现了增强
return method.invoke(instance, args); //通过反射实现需要增强的方法。
}
return method.invoke(instance, args); //不需要增强的方法放行(不一定,根据业务逻辑放行或是拦截。)
}
});
}
}
4.调用代理类:
public static void main(String[] args) throws Exception {
Object proxy = ProxyFactory.getProxy(ProxyTestInterface.class);
((ProxyTestInterface)proxy).test();
}
总结:
1.必须要通过接口实现实现类,否则无法使用代理。
2.相较静态代理更加灵活。
2.通过Cglib实现代理,不需要代理类实现任何接口。
public class ProxyFactory implements MethodInterceptor{
//维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建一个代理对象获取方法
public Object getProxyInstance(){
//1.工具类,通过这个类实现一个增强器
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
//这个回调允许你去回答调用的方法。但是你要小心使用这个回调,因为这个代理对象的所有的方法调用都会使用InvocationHandler#invoke方法,这样的话可能导致死循环。
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
//对目标方法的增强
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
}
3.调用目标对象并执行方法:
public class App {
@Test
public void test(){
//目标对象
UserDao target = new UserDao();
//代理对象
UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
//执行代理对象的方法
proxy.save();
}
}
在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理。
如果目标对象没有实现接口,用Cglib代理。
---------------------------------Generic---------------------------------------------------------------
1.泛型类Generic Class
public class Pair<T> //在类名后面定义类型变量T,可以指明局部变量,方法返回值,以及传参的类型。
E:集合的元素类型 List<E> list
K,V:键值对
T,U, S:任意的类型
2.泛型方法:
1.普通类中定义泛型方法:
class Test{
public static <T> T test(T...t){ //返回值是T类型,参数是T类型。<T>是变量限定,参见第三条,<T> T 缺一不可。
}
}
错误调用:
double ret = Test.test(1, 1.2334, 0); //1, 1.2334 and 0 are not the same type, compiler might find their mutual parent class and find Number and Comparable interface. and they are both Generic class, cause problem.
correct:
Double ret = Test.test(1D, 1.2334D, 0D);
3.泛型方法的变量限定:
在方法中使用<T>:
public static T test(T...t){ //返回值是T类型,参数是T类型。
}
<T extends Comparable&Serializable> ->限定了T必须是Comparable和Serializable的子类。
4.泛型代码和虚拟机:
1.类型擦除
无论何时定义一个泛型类型,都提供了一个相应的原始类型(raw type)。个人理解,就是将原本泛型的占位符全部替换成相应的类型,无限定变量用Object来替换。
2.翻译泛型表达式:
1.对于原始类型进行调用。(大多是在使用Object类型)
2.类型强转。将Object强转成对应的数据类型。
5.约束和局限性:
1.不能使用基本数据类型。
WRONG:
Pair<double> //擦除后会转换成Pair<Object>,而Object是类,无法存储基本数据类型,此处不会自动装箱。
2.不能创建参数化类型的数组。
个人认为数组是通过地址访问的,而不同的参数类型所占用字节数不同,无法实现index访问。
ArrayStoreException印证我的观点。
可以声明通配类型的数组然后进行类型强转。