改善Java程序的151个建议总结(七)

Don’t let complexity stop you. Be activists. Take on the big inequities. It will be one of the great experiences of your lives.

不要让这个世界的复杂性阻碍你的前进。要成为一个行动主义者,将解决人类的不平等视为己任。它将成为你生命中最重要的经历之一。

                                                                                                                         ——比尔·盖茨在哈佛大学的演讲


泛型可以减少强制类型的转换,可以规范集合的元素类型,还可以提高代码的安全性和可读性,正式因为这些优点,自从Java引入泛型后,项目的编码规则上便多了一条:优先使用泛型。

反射可以“看透”程序的运行情况,可以让我们在运行期知晓一个类或实例的运行状况,可以动态地加载和调用,虽然有一定的性能忧患,但它带给我们的便利远远大于其性能缺陷。

Java的泛型在编译期有效,在运行期被删除,也就是说所有的泛型参数类型在编译后都会被清除掉。转换规则如下:

1、List<String>、List<Integer>、List<T>擦除后的类型为List。

2、List<String>[]擦除后的类型为List[]。

3、List<? extends E>、List<? super E>擦除后的类型为List<E>。

4、List<T extends Serializable & Cloneable>擦除后为List<Serializable>。

泛型的class对象时相同的,每个类都有一个class属性,泛型化不会改变class属性的返回值。

泛型数组初始化时不能声明泛型类型,可以声明一个带有泛型参数的数组,但是不能初始化该数组。

instanceof不允许存在泛型参数。

类的成员变量是在类初始化前初始化的,所以要求在初始化前它必须具有明确的类型,否则就只能声明,不能初始化。

无法从代码中推断出泛类型的情况下,即可强制声明泛型类型。

不同场景使用不同的泛型通配符:

1、泛型结构只参与“读”操作则限定上界(使用extends关键字)

2、泛型结构只参与“写”操作则限定下界(使用super关键字)

3、泛型结构同时参与“读”“写”操作,则使用确定的泛型类型即可

协变是用一个窄类型替换宽类型,逆变是用宽类型覆盖窄类型。

Java的泛型是不支持协变和逆变的,只是能够实现协变(使用extends)和逆变(使用super),数组支持协变。

建议采用的顺序是List<T>、List<?>、List<Object>。List<T>表示的是List集合中的元素都为T类型,具体类型在运行期决定;List<?>表示的是任意类型,与List<T>类似,而List<Object>则表示List集合中所有元素为Object类型。

List<T>可以进行读写操作,List<?>是只读类型,不能进行增加、修改操作,同时List<?>读取的元素都是Object类型,需要主动转型,所以它经常用于泛型方法的返回值。List<Object>也可以进行读写操作,但是它执行写入操作时需要向上转型,在读取数据后需要向下转型。

使用“&”符号设定多重边界。它可以关联多个上界并实现多个边界限定。

当一个泛型类(特别是泛型集合)转变为泛型数组时,泛型数组的真实类型不能是泛型类型的父类型(比如顶层类Object),只能是泛型类型的子类型(当然包括自身类型),否则就会出现类型转换异常。

Java语言是先把Java源文件编译成后缀为class的字节码文件,然后通过ClassLoader机制把这些类文件加载到内存中,最后生成实例执行的,这是Java处理的基本机制。

Class类是“类中类”,有很多特殊的地方:

1、无构造函数。Class对象是在加载类时由Java虚拟机通过调用类加载器中的defineClass方法自动构造的。

2、可以描述基本类型。

3、其对象都是单例模式。

获得一个Class对象有三种途径:

1、类属性加载,如String.class。

2、对象的getClass方法,如new String().getClass()。

3、forName方法加载,如Class.forName(“java.lang.String”)

获得了Class对象后,就可以通过getAnnotations()获得注解,通过getMethod()获得方法,通过getConstructors()获得构造函数等。

getMethod方法获得的是所有public访问级别的方法,包括从父类继承的方法,而getDeclaredMethod方法获得自身类的所有方法,包括公用方法、私有方法等,而不受限于访问权限。其他的getDeclaredConstructors和getConstructors、getDeclaredFields和getFields等与此相似。

反射访问属性或方法时将Accessible设置为true。AccessibleObject是Field、Method、Constructor的父类,通过反射获取后,访问其属性与方法时,AccessibleObject类中通过override成员变量保存Accessible值,默认是false。但是在Method类中的invoke方法中通过override来判断的,如果是false,还有要进行安全检查,如果是true可以直接执行方法。这也是为了提高系统性能。

动态加载(Dynamic Loading)是指在程序运行时加载需要的类库文件,对Java程序来说,一般情况下,一个类文件在启动时或首次初始化时会被加载到内存中,而反射则可以在运行时再决定是否要加载一个类。

一个对象的生成必然会经过两个步骤:

步骤一:加载到内存中生成Class的实例对象。

步骤二:通过new关键字生成实例对象。

通过forName方法动态加载类文件,是因为一般的动态加载我们不知道生成的实例对象是什么类型,而且方法和属性都不可访问。

forName只是把一个类加载到内存中,并不保证由此产生一个实例对象,也不会执行任何方法,之所以会初始化static代码,那是由类加载机制所决定的,而不是forName方法决定的。也就是说,如果没有static属性或static代码块,forName就只是加载类,没有任何的执行行为。

数组编译对应的关系表

元素类型

编译后的类型

byte[]

[B

char[]

[C

Double[]

[D

Float[]

[F

int[]

[I

Long[]

[J

Short[]

[S

Boolean[]

[Z

引用类型(如String[])

[L引用类型(如:[Ljava.lang.String)

通过反射操作数组使用Array类,不要采用通用的反射处理API。

静态代理:通过代理主体角色(Proxy)和具体主体角色(Real Subject)共同实现抽象主体角色(Subject)的逻辑的,只是代理主体角色把相关的执行逻辑委托给了具体主体角色。

动态代理很容易实现通用的代理类,只要在InvocationHandler的invoke方法中读取持久化数据即可实现,而且还能实现动态切入的效果,这也是AOP编程理念。

public class Client {
    public static void main(String[] args) {
        // 具体主体角色,也就是被代理类
        Subject subject = new RealSubject();
        // 代理实例的处理Handler
        InvocationHandler handler = new SubjectHandler(subject);
        // 当前加载器
        ClassLoader cl = subject.getClass().getClassLoader();
        // 动态代理
        Subject proxy = (Subject) java.lang.reflect.Proxy.newProxyInstance(cl, subject.getClass().getInterfaces(), handler);
        // 执行具体主体角色方法
        proxy.request();
    }
}
// 抽象主体角色
interface Subject {
// 定义一个方法
    public void request();
}
// 具体主体角色
class RealSubject implements Subject {
    // 实现方法
    @Override
    public void request() {
        // 业务逻辑处理
    }
}

// 代理主体角色(静态代理)
class Proxy implements Subject {
// 要代理哪个实现类
    private Subject subject = null;
    // 默认被代理者
    public Proxy() {
        this.subject = new RealSubject();
    }
    // 通过构造函数传递被代理者
    public Proxy(Subject subject) {
        this.subject = subject;
    }
    // 实现接口中定义的方法
    @Override
    public void request() {
        before();
        subject.request();
        after();
    }
    // 预处理
    private void before() {
    //do something
}
    // 善后处理
    private void after() {
    //do something
}
}

// 动态代理
class SubjectHandler implements InvocationHandler {
// 被代理的对象
    private Subject subject;

    public SubjectHandler(Subject subject) {
        this.subject = subject;
    }
    // 委托处理方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
    // 预处理
        System.out.println("预处理");
    // 直接调用被代理类的方法
        Object obj = method.invoke(subject, args);
    // 后处理
        System.out.println("后处理");
        return obj;
    }
}

装饰模式(Decorator Pattern)的定义是“动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比于生成子类更为灵活”,不过,使用Java的动态代理也可以实现装饰模式的效果,而且其灵活性、适应性都会很强。

public class Client {
    public static void main(String[] args) {
        // 定义Jerry这只家喻户晓的老鼠
        Animal Jerry = new Rat();
        // 为Jerry增加飞行能力
        Jerry = new DecorateAnimal(Jerry, FlyFeature.class); 
        // 为Jerry增加钻地能力
        Jerry = new DecorateAnimal(Jerry, DigFeature.class);
        // Jerry开始耍猫了
        Jerry.doStuff();
    }
}

interface Animal {
    public void doStuff();
}
// 老鼠是一种动物
class Rat implements Animal {
    @Override
    public void doStuff() {
        System.out.println("Jerry will play with Tom");
    }
}
// 定义某种能力
interface Feature {
// 加载特性
    public void load();
}
// 飞行能力
class FlyFeature implements Feature {
    @Override
    public void load() {
        System.out.println("增加一只翅膀......");
    }
}
// 钻地能力
class DigFeature implements Feature {
    @Override
    public void load() {
        System.out.println("增加钻地能力......");
    }
}

class DecorateAnimal implements Animal {
    // 被包装的动物
    private Animal animal;
// 使用哪一个包装器
    private Class<? extends Feature> clz;
    public DecorateAnimal(Animal animal, Class<? extends Feature> clz) {
        this.animal = animal;
        this.clz = clz;
    }

    @Override
    public void doStuff() {
        InvocationHandler handler = new InvocationHandler() {
        // 具体包装行为
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
                Object obj = null;
            // 设置包装条件
                if (Modifier.isPublic(method.getModifiers())) {
                    obj = method.invoke(clz.newInstance(), args);
                }
                animal.doStuff();
                return obj;
            }
        };
        // 当前加载器
        ClassLoader cl = getClass().getClassLoader();
    // 动态代理,由Handler决定如何包装
        Feature proxy = (Feature) Proxy.newProxyInstance(cl, clz.getInterfaces(), handler);
        proxy.load();
    }
}

模板方法模式(Template Method Pattern)的定义是:定义一个操作中的算法骨架,将一些步骤延迟到子类中,使子类不改变一个算法的结构即可重定义该算法的某些特定步骤。简单地说,就是父类定义抽象模板作为骨架,其中包括基本方法(是由子类实现的方法,并且在模板方法被调用)和模板方法(实现对基本方法的调度,完成固定的逻辑),它使用了简单的继承和覆写机制。

决定使用模板方法模式时,请尝试使用反射方式实现,它会让你的程序更灵活、更强大。

abstract class AbsPopulator {
    // 模板方法
    public final void dataInitialing() throws Exception {
        // 获得所有的public方法
        Method[] methods = getClass().getMethods();
        for (Method m : methods) {
            if (isInitDataMethod(m)) {
                m.invoke(this);
            }
        }
    }
    // 判断是否是数据初始化方法,基本方法鉴别器
    private boolean isInitDataMethod(Method m) {
        return m.getName().startsWith("init") // init开始
        && Modifier.isPublic(m.getModifiers()) // 公开方法
        && m.getReturnType().equals(Void.TYPE) // 返回值是void
        && !m.isVarArgs() // 输入参数为空
        && !Modifier.isAbstract(m.getModifiers()); // 不能是抽象方法
    }
}

class UserPopulator extends AbsPopulator {
    public void initUser() {
        // 初始化用户表,如创建、加载数据等
    }
    public void initPassword() {
        // 初始化密码
    }
    public void initJobs() {
        // 初始化工作任务
    }
}

反射效率低是个真命题,但因为这一点而不使用它就是个假命题。

class Utils {
    // 获得一个泛型类的实际泛型类型
    public static <T> Class<T> getGenricClassType(Class clz) {
        Type type = clz.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) type;
            Type[] types = pt.getActualTypeArguments();
            if (types.length > 0 && types[0] instanceof Class) {
                // 若有多个泛型参数,依据位置索引返回
                return (Class) types[0];
            }
        }
        return (Class) Object.class;
    }
}

abstract class BaseDao<T> {
    // 获得T的运行期类型
    private Class<T> clz = Utils.getGenricClassType(getClass());
    // 根据主键获得一条记录
    public void get(long id) {
        session.get(clz, id);
    }
}
// 操作user类
class UserDao extends BaseDao<String> {
    
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值