update final

master
Omooo 6 years ago
parent 68f472825e
commit fb1b3a8a53
  1. 5
      README.md
  2. 5
      blogs/AOP_AspectJ.md
  3. 90
      blogs/Question.md
  4. 11
      blogs/String.md
  5. 106
      blogs/final.md

@ -13,5 +13,10 @@ Android Notes
- [SharedPreferences](https://github.com/Omooo/Android-Notes/blob/master/blogs/SharedPreferences.md)
- [SQLite](https://github.com/Omooo/Android-Notes/blob/master/blogs/SQLite.md)
2. [埋点](https://github.com/Omooo/Android-Notes/blob/master/blogs/%E5%9F%8B%E7%82%B9.md)
#### Java
[final 你需要知道的一切](https://github.com/Omooo/Android-Notes/blob/master/blogs/final.md)
[String 你需要知道的一切](https://github.com/Omooo/Android-Notes/blob/master/blogs/String.md)

@ -18,6 +18,7 @@ AOP 之 AspectJ
- 全局点击事件拦截
- 方法统计耗时
- 权限申请
- 缺点
4. 参考
#### 思维导图
@ -202,7 +203,11 @@ ProceedingJoinPoint 用于环绕通知,它是 JointPoint 的子类,与其不
以上两个示例都是按照相似性统一切,而通过自定义注解修饰切人点,能够达到精确切。
##### 缺点
1. 无法织入第三方的库
2. 无法兼容 Lambda 语法
3. 会有一些兼容性问题,比如 D8、Gradle 4.x 等
#### 参考

@ -1,90 +0,0 @@
```java
/**
* 我想统计两个类中方法执行耗时,BootActivity 中的统计没什么问题,
* 但是 CommonRequest 类中的统计直接导致应用崩溃
* 类转换异常?很萌
* AspectJ 才学,希望大佬能帮帮我,不胜感激
*/
com.xxx.BootActivity
public class BootActivity extends Activity{
onCreate(){
CommonRequest.method1(this);
CommonRequest.method2(this);
//...
}
}
com.yyy.CommonRequest
public class CommonRequest{
public static void mothod1(Context context){
//网络请求
new HttpRequest(context).callback(new CallBack()){
//
}
}
}
//切面类
@Aspect
public class MethodTimeTracker {
//这个切点执行正常
@Around("execution( * com.xxx.BootActivity.* (..))")
public void bootTracker(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = SystemClock.currentThreadTimeMillis();
joinPoint.proceed();
long endTime = SystemClock.currentThreadTimeMillis();
long dx = endTime - beginTime;
Log.i("2333", joinPoint.getSignature().getDeclaringType().getName() + "#" + joinPoint.getSignature().getName()
+ " " + dx + " ms");
}
//这个切点直接导致应用崩溃
@Around("execution( * com.yyy.CommonRequest.* (..))")
public void requestTracker(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = SystemClock.currentThreadTimeMillis();
joinPoint.proceed();
long endTime = SystemClock.currentThreadTimeMillis();
long dx = endTime - beginTime;
Log.i("2333", joinPoint.getSignature().getDeclaringType().getName() + "#" + joinPoint.getSignature().getName()
+ " " + dx + " ms");
}
}
Log 日志:
java.lang.RuntimeException: Unable to start activity ComponentInfo{.BootActivity}: java.lang.ClassCastException: BootActivity cannot be cast to cn.shuhe.dmnetwork.network.CjjHttpRequest
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.ClassCastException: BootActivity cannot be cast to CjjHttpRequest
at cn.shuhe.projectfoundation.CommonRequest$AjcClosure15.run(CommonRequest.java:1)
at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:149)
at com.dataseed.cjjanalytics.aspect.MethodTimeTracker.requestTracker(MethodTimeTracker.java:29)
at cn.shuhe.projectfoundation.CommonRequest.checkDeviceInfo(CommonRequest.java:223)
at com.dataseed.huanbei.ui.BootActivity.startUp(BootActivity.java:296)
at com.dataseed.huanbei.ui.BootActivity.onCreate_aroundBody2(BootActivity.java:129)
at com.dataseed.huanbei.ui.BootActivity$AjcClosure3.run(BootActivity.java:1)
at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:149)
at com.dataseed.cjjanalytics.aspect.CjjTraceAspect.weaveActivityOnCreateJoinPoint(CjjTraceAspect.java:225)
at com.dataseed.huanbei.ui.BootActivity.onCreate(BootActivity.java:126)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
类转换异常是怎么肥事?
```

@ -0,0 +1,11 @@
---
String
---
#### 目录
1. 思维导图
2. String
-
3. 参考

@ -0,0 +1,106 @@
---
final
---
#### 目录
1. final
- 引言
- 内存语义
- 优势所在
- 天生线程安全
- 编译器优化
- 参考
#### 引言
说起 final 关键字,脱口而出就是修饰变量变量不可变、修饰方法方法不可重写、修饰类类不可继承。
或许你还听过 final 能够提高效率,那如何提高效率的呢?方法内联?方法内联又是个什么东西呢?
带着疑问来探索一下 final。
#### 内存语义
##### 写 final 域的重排序规则
写 final 域的重排序规则禁止把 final 域的写重排序到构造函数之外,这个规则的实现包含两个方面:
1. JVM 禁止编译器把 final 域的写重排序到构造函数之外
2. 编译器会在 final 域的写之后,构造函数 return 之前,插入一个 StoreStore 内存屏障,这个屏障禁止处理器把 final 域的写重排序到构造函数之外
写 final 域的重排序规则可以确保:在对象引用为任何线程可见之前,对象的 final 域已经被正确初始化过了,而普通域不具有这个保证。
##### 读 final 域的重排序规则
在一个线程中,初次读对象引用与初次读改对象包含的 final 域,JMM 禁止处理器重排序这两个操作。编译器会在读 final 域操作的前面插入一个 LoadLoad 内存屏障。
读 final 域的重排序可以确保:在读一个对象的 final 域之前,一定会先读包含这个 final 域的对象引用。
#### 优势
##### 线程安全
这个不用多说,不可变对象天生线程安全。
##### 编译器优化
经常能听到别人说 final 能提高效率,在于编译器在对 final 修饰的方法做了方法内联。这句话对与否先不讨论,先来了解一下方法内联是什么鬼?
在了解方法内联之前,先来了解一下函数调用过程:
1. 首先会有个执行栈,存储它们的局部变量、方法名、动态链接地址
2. 当一个方法调用,一个新的栈桢会被加到栈顶,分配的本地变量和参数会存储在这个栈桢
3. 调转到目标方法代码执行
4. 方法返回的时候,本地方法和参数被销毁,栈顶被移除
5. 返回原来的地址执行
其实这就是虚拟机栈执行方法的内存模型。函数调用需要一定的时间开销和空间开销,当一个方法被频繁调用时,这个时间和空间开销会相对变的很大。根据二八原则,80% 的性能消耗其实是发生在 20% 的代码上,对热点代码的针对优化可以提升整体系统的性能。
然后我们在看看方法内联到底是什么?其实可以用以下例子粗鄙的解释:
```java
private int add2(int x1 , int x2 , int x3 , int x4) {
return add1(x1 , x2) + add1(x3,x4);
}
private int add1(int x1 , int x2) {
return x1 + x2;
}
```
在运行一段时间后,代码可能就变成下面这样了:
```java
private int add2(int x1 , int x2 , int x3 , int x4) {
//return add1(x1 , x2) + add1(x3,x4);
return x1 + x2 + x3 + x4;
}
```
嘿,看来方法内联就是把调用的方法函数代码复制到了调用函数内,减少因函数调用而带来的开销。
但是方法内联也是有条件的:
1. 热点代码
2. 方法体不能太大
方法内联会导致拷贝代码副本过多,代码占用内存增加,所以方法体不能太大。
其次由于方法可能被继承,导致需要类型检查而没有达到性能的效果,所以想要对热点代码进行方法内联,最好尽量使用 final、private、static 这些修饰方法,避免因为继承而导致额外的类型检查。
说到这,其实就能理解 final 能够提高效率的原因在于哪了,**但是如果想通过 final 来提升性能,那几乎是不太可能。**
现在,让我来回答一下 final 能够提高性能这个说法?
emmmm,这样的说法太片面。final 的确能够提升性能,但毕竟微乎其微。final 修饰的变量值多数情况下就能在编译阶段确定下来,这就可能导致某些代码被编译器优化掉。其次,关于方法内联的说法,也的确存在。但是方法内联也是需要条件的,首先是被频繁调用的热点代码,方法内联能够减少方法调用所带来的性能消耗;其次是方法体不能过大,原因很简单,方法内联将会导致拷贝代码更多,代码占用更多的内存空间。最后才是方法最好能被 final、private、static 修饰,这样就能避免过多的参数检查、类型检查等等。我们更应该关注 final 所带来的特性,比如属性不可变、方法不可重写、类不可继承,想要通过 final 来提升性能,几乎是不可能的事。
#### 参考
[深入理解Java中的final关键字](http://www.importnew.com/7553.html)
[Final of Java,这一篇差不多了](https://www.jianshu.com/p/f68d6ef2dcf0)
[final修饰递归方法会提高效率吗?](https://www.zhihu.com/question/66083114)
[Java 方法内联](https://www.cnblogs.com/xyz-star/p/10152564.html)
Loading…
Cancel
Save