美文网首页
About Java

About Java

作者: dx8719 | 来源:发表于2021-05-26 12:37 被阅读0次

泛型

可以将运行期的错误转移到编译器来

泛型是JDK5中引入的一种参数化类型特性

把类型当作参数一样传递

<数据类型>只能是引用类型(泛型的副作用)

Plate<T>中的T称为类型参数

Plate<Banana> Banana称为实际类型参数

1.代码更加健壮,只要编译期没有警告,那么运行期就不会出现ClassCastExecption

2.代码更加简洁(不用强转)

3.代码更灵活 复用

Java是如何处理泛型的 泛型类的本质

如果没有指定类型,会被擦除成为Object类型32

@GET(“xxx/xxx")

Call<Apple>getApple()

擦除了但仍然保存在类的常量池,可以获取到Type信息

泛型是jdk5引入的,为了向下兼容,实际上虚拟机是不支持泛型的,所以Java实现的是一种伪泛型机制,也就是说在编译时期擦出了所有的泛型信息,这样Java就不需要产生的新的类型到字节码,所有的泛型最终都是一种原始类型,在Java运行时根本不存在泛型信息。

具体是如何擦除泛型类型的?

在不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一父类的最小级,直到Object

在指定泛型的情况下,该方法的几种类型必须是该泛型的实例的类型或者其子类

PECS原则: 如果只需要从集合中获取类型T 使用<? extends T>通配符

如果只需要将类型T放到集合中 使用<? super T> 通配符

使用通配符只有一个目的:灵活转型

注解和反射

注解的作用:本身么有任何意义,单独的注解就是一种注释。 它需要集合其他如反射、插桩等技术才有意义

元注解:注解上的注解 Target :指明注解作用的位置/节点

Retention:声明保留级别:

RetentionPolicy.SOURCE  -保留到源码阶段  APT技术:注解处理器,一般用于生成额外的辅助类,运行在编译阶段。  .java -> javac ->.class  javac采集到所有的注解信息

继承AbstractProcessor -> 注册到配置文件(手动创建) ->写全类名到文件

实现process方法-> message = processingEnv.getMessager

RetentionPolicy.CLASS 保留到class(字节码)阶段 ,字节码增强技术-热修复

字节码增强:在字节码(.class)中写代码

RetentionPolicy.RUNTIME 保留到运行时

SOURCE<CLASS<RUNTIME

注解处理程序:是javac ->class阶段执行

IntDef注解 语法检查

@IntDef 也是一种元注解

@IntDef (可以限定)

IDE的语法检测也是类似的原理

RUNTIME级别 -反射

反射:在运行时只需要知道类的信息,而不需要知道类对象和去new一个类。

注解+反射 :自动完成findViewById()

Field:获取自己+父类的成员(不包括private,只能public)

DeclaredField:只能获取自己的成员(不包括父类)

1.为要处理的view打标签

作业: 使用注解+反射实现自动取得Intent参数功能

String StringBuffer StringBuilder区别

String是不可变对象,字符串常量,对该字符串操作会产生一个新的常量

StringBuffer和StringBuilder是字符串变量,每次操作都是对本身的字符串操作,Buffer是线程安全,而后者是线程不安全的,也因此Buffer的效率更低。

少量字符串:String

单线程大量字符串:StringBuilder

多线程大量字符串:StringBuffer

强引用>软引用>弱引用>虚引用

强:不会被垃圾回收,对象的一般状态,JVM停止运行时终止

软:内存不足时回收,对象缓存,内存不足时终止

弱:正常GC时,对象缓存,垃圾回收后终止

虚:正常GC时,跟踪对象的垃圾回收,垃圾回收后终止

Handler.removeCallbacksAndMessages(null)方便快捷,在onDestroy里面Handler.removeCallbacksAndMessages(null);,无论runnbale还是message消息全清空,自然也不会关联上Activity。下次GC就能顺利回收了。

代理模式

比如要将Volley框架切换到Okhttp框架,如果使用代理模式,只需要修改代理即可。代理模式起到了隔离的作用,

通过一个代理类,实现全部的代理功能,就需要使用动态代理了。

Retrofit 使用动态代理获取ApiService的代理 ->反射获取方法上的注解/方法参数上的注解 ->记录下这些参数 ->构建okHttp请求

利用反射,注解,动态代理实现OnClick的注入

volatile Object mData

volatile  多个线程 -> 工作内存 ->操作主内存。  volatile保证数据的一致性

notification消息过来,有三个Activity可以处理这个消息,boolean值标记是否被处理。但偶现有一个页面在子线程处理导致数据不一致。 这个时候使用volatile标记bool值就好了。

轮询所有观察者 回调onChange函数

lifeCycle

组件化:合理的组件是如何定义的

liveData二次封装

线程和进程

进程:操作系统管理的最小单元

线程:CPU调度的最小单元

1进程有1个或多个线程

线程依附进程

并行:两个跑道

并发:时间,吞吐量,比如10秒内服务器的吞吐量,10秒钟多少车流量

高并发编程:平衡使用线程,不要CPU过累

new Thread() 每次开辟栈空间,每次至少1MB  result:1GB内存

Java默认就是多线程

三种启动方式 Thread.start() 

Runnable

Callable

run()和start()区别?

run是函数调用,和线程没有任何关系。run包裹的部分叫做线程体

start 会调用native方法 - 调用系统的线程操作

.start会走底层 ->系统层 ->最终调用到run执行线程体内的函数 这才是线程

加锁后,其他线程无法进来,执行后解锁释放,解决安全性问题。

notify()唤醒wait()中的线程

wait: 获取对象的锁,持有对象的锁,所以要包裹在syn中

CAS操作 1:31:37

ThreadLocal:线程本地变量,让每个线程拥有属于自己的变量的副本

线程池:使用阻塞队列管理任务

先在corePoolSize数量之内承接任务, 超过就把任务放在阻塞队列中,如果阻塞队列依然被填满,则新启动线程执行任务,线程数小于最大线程数,如果最大线程数也满了,就启动拒绝策略。

四种拒绝策略Handler:

1.直接丢弃最老的

2.(默认)直接抛出异常

3.让调用者线程执行任务

4.丢弃最新提交的任务

shutDown线程池:发送中断信号

shutDownNow:发送中断信号,取决于线程内任务对中断信号的处理,也不一定会立即关闭

线程池:配置取决于任务特性:

CPU密集型:CPU核心数:Runtime.getRuntime().avaliableProcessors();  最多+1,比核心数多1(可能虚拟内存,避免CPU出现空闲)

IO密集型:经验值:CPU核心数*2

混合型

面试题:资料中有 看一下***

AQS: AbstractQueuedSynchronizer  同步工具类的内部,内部类继承AQS 功能

volatile:可见性(对于一个volatile变量的读,总是能看到任意线程对这个volatile变量最后的写入)。原子性:对任意单个volatile变量的读写具有原子性,但复合操作不具有原子性

实现原理:使用CPU提供的lock前缀指令,可以强迫当前CPU对变量的修改写回到系统内存,使其他CPU里缓存了该内存地址的数据无效。

synchronized关键字的原理:

Java虚拟机会插入字节码指令,monitorenter,monitorexit,enter会插入到同步代码块开始的位置,ext会插入到结束的位置

拿到Monitor对象的所有权,就表示进入了锁,执行完成后释放了所有权,表示释放了锁。

轻量级锁:通过CAS来加锁解锁 自旋锁 适应性自旋锁 :虚拟机自行动态判定自旋次数:大概是一个线程的上下文切换所需时间,一个锁总是同一个线程获得:偏向锁

序列化

Parcelable性能更好,Serializable在序列化时会产生大量的临时变量(由于它大量使用反射),从而引起频繁的GC

而Parcelable底层使用了binder,Android专有,一般用于进程间通信

Serializable用于网络间通信

但Parcelable不能使用在将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性,在外界有变化的情况下建议使用Serializable

1.反序列化的对象无须再调用构造函数构造,因为使用二进制数据构造了

2.序列化前后的对象,是两个不同的对象,对象的地址是变了的,但是使用equals比较是true(深拷贝)

Intent传递数据需要序列化,实际上是经过了AMS操作来启动Activity,实际上是跨进程操作。

序列化主要是为了跨进程传输,而持久化是关注将数据存储

Bundle主要是使用了Parcelable打包数据,因为Bundle也是用于跨进程传输对象的(小对象)

JSON解析:

每种数据类型都有对应的TypeAdapter,如需自定义可以自定义Adapter

文件操作

APK加固:dex文件加密,文件IO stream IO

将dex文件与壳dex文件合并后打包到apk中

1.壳文件从何而来?

2.dex文件可以随便拼凑吗?

3.如何签名?

4.如何运行新的apk(如何脱壳 )

DEX文件(文件头、索引区、数据区(类定义区、数据区、链接数据区))

apk打包流程:合并到dex文件 ->打包到apk ->签名 :META-INF目录下是签名文件

加密过程:源apk ->解压并过滤查找到dex文件 -> 使用fis.readFully()获取到dex文件的所有字节 ->使用AES对字节加密 ->写回到dex文件 ->重命名dex文件

找到壳dex文件

JVM

跨平台:Java - Java字节码 -操作系统函数

跨语言,Kotlin等

相关文章

网友评论

      本文标题:About Java

      本文链接:https://www.haomeiwen.com/subject/nnbedltx.html