美梦&热望


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

泛型

发表于 2019-01-16

泛型

引言

Java语言引入泛型的好处是安全简单

public static void main(String[] args) {
        List list = new ArrayList();
        list.add("qqyumidi");
        list.add("corn");
        list.add(100);

        for (int i = 0; i < list.size(); i++) {
            String name = (String) list.get(i); // 1
            System.out.println("name:" + name);
        }
}

定义了一个List类型的集合,先向其中加入了两个字符串类型的值,随后加入一个Integer类型的值。
这是完全允许的,因为此时list默认的类型为Object类型。
在之后的循环中,由于忘记了之前在list中也加入了Integer类型的值或其他编码原因,很容易出现类似于//1中的错误。
因为编译阶段正常,而运行时会出现“java.lang.ClassCastException”异常。因此,导致此类错误编码过程中不易发现。

那么有没有什么办法可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现“java.lang.ClassCastException”异常呢?

答案就是使用泛型

在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,
“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。
对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率

什么是泛型

泛型,即“参数化类型”。
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。
那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

List<String> list = new ArrayList<String>();
  list.add("qqyumidi");
  list.add("corn");
  //list.add(100);   // 1  提示编译错误

  for (int i = 0; i < list.size(); i++) {
      String name = list.get(i); // 2
      System.out.println("name:" + name);
  }

采用泛型写法后,在//1处想加入一个Integer类型的对象时会出现编译错误,通过List,直接限定了list集合中只能含有String类型的元素,
从而在//2处无须进行强制类型转换,因为此时,集合能够记住元素的类型信息,编译器已经能够确认它是String类型了。

结合上面的泛型定义,我们知道在List中,String是类型实参,也就是说,相应的List接口中肯定含有类型形参。
且get()方法的返回结果也直接是此形参类型(也就是对应的传入的类型实参)。下面就来看看List接口的的具体定义:

public interface List<E> extends Collection<E> {
    ......
}

我们可以看到,在List接口中采用泛型化定义之后,中的E表示类型形参,可以接收具体的类型实参,并且此接口定义中,凡是出现E的地方均表示相同的接受自外部的类型实参.

三.自定义泛型接口、泛型类和泛型方法

从上面的内容中,大家已经明白了泛型的具体运作过程。也知道了接口、类和方法也都可以使用泛型去定义,以及相应的使用。

是的,在具体使用时,可以分为泛型接口、泛型类和泛型方法。

List等集合默认是object类

自定义泛型接口、泛型类和泛型方法与上述Java源码中的List、ArrayList类似。如下,我们看一个最简单的泛型类和方法定义:

public class GenericTest {
public static void main(String[] args) {\
    Box<String> name = new Box<String>("corn");
    System.out.println("name:" + name.getData());
}
}

class Box<T> {
private T data;
public Box() {
}
public Box(T data) {
    this.data = data;
}
public T getData() {
    return data;
}
}

在泛型接口、泛型类和泛型方法的定义过程中,我们常见的如T、E、K、V等形式的参数常用于表示泛型形参,由于接收来自外部使用时候传入的类型实参。
那么对于不同传入的类型实参,生成的相应对象实例的类型是不是一样的呢?

使用大写字母A,B,C,D……X,Y,Z定义的,就都是泛型,把T换成A也一样,这里T只是名字上的意义而已

? 表示不确定的java类型
T (type) 表示具体的一个java类型
K V (key value) 分别代表java键值中的Key Value
E (element) 代表Element

类型通配符上限通过形如Box<? extends Number>形式定义,相对应的,
类型通配符下限为Box<? super Number>形式,其含义与类型通配符上限正好相反,在此不作过多阐述了

相关链接

https://blog.csdn.net/qq_31433709/article/details/79234367

https://www.cnblogs.com/lwbqqyumidi/p/3837629.html

JNDI使用

发表于 2019-01-16

JNDI使用

应用场景

通过javaagent方式引入增强包,对JVM中的用户自定义类的方法(写在应用中的某个配置文件)进行增强,在Tomcat容器中启动时报无法找到配置文件的异常,通过对比Worker类型的应用,发现ClassLoader类型及加载机制不同造成。

  • Tomcat 类型使用WebAppClassLoader加载应用,配置文件可以通过WebAppClassLoader可以获取;
  • Worker类型使用AppClassLoader加载应用,配置文件可以通过AppClassLoader获取。
    上述两种类型中,javaagent方式引入的增强包都在AppClassLoader中,从而导致Tomcat类型应用在启动时无法找到WebAppClassLoader中的配置文件,导致报异常。

解决方案

方案 一

  • 同用户约定待增强方法的配置文件目录路径,然后在增强包中写死,程序运行时按约定路径去取配置文件;
  • 开发时发现,J-one/jdos 对Tomcat类型提供的配置文件路径为相对路径,及后半部分路径,只提供给用户WEB-INF/后面的路径供用户选填写。由于每个应用的前半部分路径每个都不一样,所以导致无法同用户约定目录路径。

方案二

采用JNDI 方式,打破双亲委派的ClassLoader的加载机制,在Tomcat启动后,在增强包中获取WebAppClassLoader类加载器,从而获取到用户的配置文件。

classLoader原理

jvm有三种加载器,每种加载器负责特定目录的类。

  • 根类加载器(BootStrap)和扩展加载器(Extension)不必说,它们负责加载的都是特定目录的类,而且这些类是固定的,我们不能说我们把自己编写的类加到java.lang目录下就能使这个类被根类加载器加载,那样就太不安全了。因为加载的类是固定的就很好理解,
  • 但是系统类加载器(System)就比较不好理解了,它是加载classpath定义的目录下的类
12
Levin

Levin

岁月如冰河,热望如鲸歌

12 日志
3 分类
3 标签
© 2019 Levin
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4