编写高质量代码:改善Java程序的151个建议 --[98~105]
建议的采用顺序是List中泛型顺序依次为T、?、Object
(1)、List是确定的某一个类型
List表示的是List集合中的元素都为T类型,具体类型在运行期决定;List<?>表示的是任意类型,与List类似,而List则表示List集合中的所有元素为Object类型,因为Object是所有类的父类,所以List也可以容纳所有的类类型,从这一字面意义上分析,List更符合习惯:编码者知道它是某一个类型,只是在运行期才确定而已。
(2)List可以进行读写操作
List可以进行诸如add,remove等操作,因为它的类型是固定的T类型,在编码期不需要进行任何的转型操作。
List是只读类型的,不能进行增加、修改操作,因为编译器不知道List中容纳的是什么类型的元素,也就无法校验类型是否安全了,而且List<?>读取出的元素都是Object类型的,需要主动转型,所以它经常用于泛型方法的返回值。注意List<?>虽然无法增加,修改元素,但是却可以删除元素,比如执行remove、clear等方法,那是因为它的删除动作与泛型类型无关。
List 也可以读写操作,但是它执行写入操作时需要向上转型(Up cast),在读取数据的时候需要向下转型,而此时已经失去了泛型存在的意义了。
严格限定泛型类型采用多重界限
List接口的toArray方法可以把一个集合转化为数组,但是使用不方便,toArray()方法返回的是一个Object数组,所以需要自行转变。 当一个泛型类(特别是泛型集合)转变为泛型数组时,泛型数组的真实类型不能是泛型的父类型(比如顶层类Object),只能是泛型类型的子类型(当然包括自身类型),否则就会出现类型转换异常。 通过反射类Array声明了一个T类型的数组,由于我们无法在运行期获得泛型类型的参数,因此就需要调用者主动传入T参数类型。 List转数组:
public static <T> T[] toArray(List<T> list,Class<T> tClass) {
//声明并初始化一个T类型的数组
T[] t = (T[])Array.newInstance(tClass, list.size());
for (int i = 0, n = list.size(); i < n; i++) {
t[i] = list.get(i);
}
return t;
}
注意Class类的特殊性
class类的特殊性:
无构造函数:Java中的类一般都有构造函数,用于创建实例对象,但是Class类却没有构造函数,不能实例化,Class对象是在加载类时由Java虚拟机通过调用类加载器中的difineClass方法自动构造的。 可以描述基本类型:虽然8个基本类型在JVM中并不是一个对象,它们一般存在于栈内存中,但是Class类仍然可以描述它们,例如可以使用int.class表示int类型的类对象。 其对象都是单例模式:一个Class的实例对象描述一个类,并且只描述一个类,反过来也成立。一个类只有一个Class实例对象
获得Class对象的三种途径:
类属性方式:如String.class 对象的getClass方法,如new String().getClass() forName方法加载:如Class.forName(" java.lang.String") 获得了Class对象后,就可以通过getAnnotations()获得注解,通过getMethods()获得方法,通过getConstructors()获得构造函数等
适时选择getDeclaredXXX和getXXX
getXXX法获得的是所有public访问级别的方法,包括从父类继承的方法,而getDeclaredXXX获得的是自身类的方法,包括公用的(public)方法、私有(private)方法,而且不受限于访问权限。 如果需要列出所有继承自父类的方法,该如何实现呢?简单,先获得父类,然后使用getDeclaredMethods,之后持续递归即可。
反射访问属性或方法时将Accessible设置为true
通过反射执行一个方法的过程如下:
获取一个方法对象;
然后根据isAccessible返回值确定是否能够执行,如果返回值为false则需要调用setAccessible(true);
最后再调用invoke执行方法
Method method= ...;
//检查是否可以访问
if(!method.isAccessible()){
method.setAccessible(true);
}
//执行方法
method.invoke(obj, args);
AccessibleObject源码:
public class AccessibleObject implements AnnotatedElement {
//定义反射的默认操作权限suppressAccessChecks
static final private java.security.Permission ACCESS_PERMISSION =
new ReflectPermission("suppressAccessChecks");
//是否重置了安全检查,默认为false
boolean override;
//构造函数
protected AccessibleObject() {}
//是否可以快速获取,默认是不能
public boolean isAccessible() {
return override;
}
}
AccessibleObject是Filed、Method、Constructor的父类,决定其是否可以快速访问而不进行访问控制检查,在AccessibleObject类中是以override变量保存该值的,但是具体是否快速执行时在Method的invoke方法中决定的:
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
//是否可以快速获取,其值是父类AccessibleObject的override变量
if (!override) {
//不能快速获取,执行安全检查
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass(1); checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
//直接执行方法
return ma.invoke(obj, args);
}
Accessible属性只是用来判断是否需要进行安全检查的,如果不需要则直接执行,这就可以大幅度的提升系统性能了(当然了,取消了安全检查,也可以运行private方法、访问private属性的)。经过测试,在大量的反射情况下,设置Accessible为true可以提高性能20倍左右。 Accessible属性决定Field和Constructor是否受访问控制检查。我们在设置Field或执行Constructor时,务必要设置Accessible为true。
使用forName动态加载类文件
动态加载(Dynamic Loading)是指在程序运行时加载需要的类库文件,对Java程序来说,一般情况下,一个类文件在启动时或首次初始化时会被加载到内存中,而反射则可以在运行时再决定是否需要加载一个类。 举个栗子:
public class Client103 {
public static void main(String[] args) throws ClassNotFoundException {
//动态加载
Class.forName("com.study.advice103.Utils");
}
}
class Utils{
//静态代码块
static{
System.out.println("Do Something.....");
}
}
结果: Do Something.....
forName只是把一个类加载到内存中,并不保证由此产生一个实例对象,也不会执行任何方法,之所以会初始化static代码,那是由类加载机制所决定的,而不是forName方法决定的。也就是说,如果没有static属性或static代码块,forName就是加载类,没有任何的执行行为。
动态加载不适合数组
数组是一个非常特殊的类,虽然它是一个类,但没有定义类类路径。
String [] strs = new String[10];
Class.forName("java.lang.String[]");
会产生bug:
Exception in thread "main" java.lang.ClassNotFoundException: java/lang/String[]
at java.lang.Class.forName0(Native Method)

动态加载数组:
// 动态创建数组
String[] strs = (String[]) Array.newInstance(String.class, 8);
// 创建一个多维数组
int[][] ints = (int[][]) Array.newInstance(int.class, 2, 3);
编写高质量代码:改善Java程序的151个建议 --[98~105]的更多相关文章
- 博友的 编写高质量代码 改善java程序的151个建议
编写高质量代码 改善java程序的151个建议 http://www.cnblogs.com/selene/category/876189.html
- 编写高质量代码改善java程序的151个建议——导航开篇
2014-05-16 09:08 by Jeff Li 前言 系列文章:[传送门] 下个星期度过这几天的奋战,会抓紧java的进阶学习.听过一句话,大哥说过,你一个月前的代码去看下,慘不忍睹是吧.确实 ...
- 编写高质量代码改善java程序的151个建议——[1-3]基础?亦是基础
原创地址: http://www.cnblogs.com/Alandre/ (泥沙砖瓦浆木匠),需要转载的,保留下! Thanks The reasonable man adapts himse ...
- 编写高质量代码:改善Java程序的151个建议 --[117~128]
编写高质量代码:改善Java程序的151个建议 --[117~128] Thread 不推荐覆写start方法 先看下Thread源码: public synchronized void start( ...
- 编写高质量代码:改善Java程序的151个建议 --[106~117]
编写高质量代码:改善Java程序的151个建议 --[106~117] 动态代理可以使代理模式更加灵活 interface Subject { // 定义一个方法 public void reques ...
- 编写高质量代码:改善Java程序的151个建议 --[78~92]
编写高质量代码:改善Java程序的151个建议 --[78~92] HashMap中的hashCode应避免冲突 多线程使用Vector或HashTable Vector是ArrayList的多线程版 ...
- 编写高质量代码:改善Java程序的151个建议 --[65~78]
编写高质量代码:改善Java程序的151个建议 --[65~78] 原始类型数组不能作为asList的输入参数,否则会引起程序逻辑混乱. public class Client65 { public ...
- 编写高质量代码:改善Java程序的151个建议 --[52~64]
编写高质量代码:改善Java程序的151个建议 --[52~64] 推荐使用String直接量赋值 Java为了避免在一个系统中大量产生String对象(为什么会大量产生,因为String字符串是程序 ...
- 编写高质量代码:改善Java程序的151个建议 --[36~51]
编写高质量代码:改善Java程序的151个建议 --[36~51] 工具类不可实例化 工具类的方法和属性都是静态的,不需要生成实例即可访 问,而且JDK也做了很好的处理,由于不希望被初始化,于是就设置 ...
随机推荐
- artTemplate精彩文章(个人阅读过)
轻量级artTemplate引擎 实现前后端分离—基础篇 :https://www.imooc.com/article/20263 轻量级artTemplate引擎 实现前后端分离—语法篇 : htt ...
- 剑指offer(11)
题目: 输入一个链表,输出该链表中倒数第k个结点. 思路: 我们一先想到的应该是循环两次链表,第一次获得它的长度,然后用长度-k,得出目标节点在链表的第几位,再循环一次. 如果要求只用一次循环的话,我 ...
- vue-cli3.0之vue.config.js的配置项(注解)
module.exports = { // 部署应用时的基本 URL baseUrl: process.env.NODE_ENV === 'production' ? '192.168.60.110: ...
- 关于@Param
1,使用@Param注解 当以下面的方式进行写SQL语句时: @Select("select column from table where userid = #{userid} " ...
- Prism框架研究(二)
首先在介绍本节内容之前,首先来看看官方文档来如何描述Prism 应用的初始化吧!A Prism application requires registration and configuration ...
- C#中List<T>排序
在面向对象开发过程中我们经常将一组对象放到一个特定集合中,此时我们通常使用泛型集合来存放,常见的如:List.Dictionary等.在使用这些泛型集合时我们有时需要对其进行排序,下面我们就一起学习下 ...
- Shell 编程和Python编程的那些不同之处(一)
循环 shell中for循环的表现形式: 1.数字段形式 for i in {1..10};do echo $i;done 还支持按规定的步数进行跳跃的方式实现列表for循环,例如计算1-100内所 ...
- 常见IT工具软件总结
1. 阿里云在线迁移服务 2.智能媒体管理 格式转换 业务域名管理 1. 每个业务有一个英文单词, 1. 每个 git 的命名应该是 chgg-业务英文-种类 2. 例如 chgg-plant-api ...
- c#处理json数据最好的方式,没有之一。
c#处理json数据最好的方式,没有之一. 引用Json.Net(需要.NET 4.5及以上版本) using Newtonsoft.Json.Linq; 使用非常简单 JObject result ...
- codeforces263B
Squares CodeForces - 263B Vasya has found a piece of paper with a coordinate system written on it. T ...