Java中泛型

2023-11-12

==《Thinking in Java》 第15章笔记==

即使使用了接口,就要求代码必须使用特定的接口,对程序的约束也还是太强了。我们希望达到的目的是编写更通用的代码,要使代码能够应用与“某种不具体的类型”,而不是一个具体的接口或类。泛型这个术语的意思就是适用于许多许多的类型。

  • 15.1 与C++的比较
public class Holder<T>{
    private T a;
    ...
}

T就是类型参数,Java泛型的核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切细节。

  • 15.2.1 一个元祖类库

元祖:将一组对象直接打包存储于其中的一个单一对象。

public class TwoTuple<A,B>{
    public final A first;
    public fianl B second;
    public TwoTuple(A a, B b){
        first = a;
        second = b;
    }
}

通过final关键字保证安全性,可以随心所欲的使用这两个对象,却无法改变这两个对象。

  • 15.3 泛型接口

例如生成器,是工厂方法设计模式的一种应用。

public interface Generator<T> {
    T next();
}

public class CoffeeGenerator implements Generator<Coffee>, Iterable<Coffee> {
    private Class[] types = {Latte.class, Mocha.class, Cappuccino.class, Americano.class, Breve.class};
    private static Random random = new Random(44);
    public CoffeeGenerator() {}
    private int size = 0;
    public CoffeeGenerator(int sz) {size = sz;}
    @Override
    public Coffee next() {
        try{
            return (Coffee) types[random.nextInt(types.length)].newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    class CoffeeIterator implements Iterator<Coffee>{
        int count = size;

        @Override
        public boolean hasNext() {
            return count > 0;
        }

        @Override
        public Coffee next() {
            count --;
            return CoffeeGenerator.this.next();
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
    public Iterator<Coffee> iterator(){
        return new CoffeeIterator();
    }

    public static void main(String[] args){
        CoffeeGenerator gen = new CoffeeGenerator();
        for(int i = 0; i < 5; i++)
            System.out.println(gen.next());
        for(Coffee c : new CoffeeGenerator(5))
            System.out.println(c);
    }
}

注意:基本类型无法作为类型参数,不过Java SE5具备了自动打包和自动拆包的功能,可以很方便地在基本类型和其相应的包装器类型之间进行转换。

  • 15.4 泛型方法

是否拥有泛型方法,与其所在的类是否是泛型没有关系,可以是泛型类,也可以不是泛型类。

如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,因为它可以使事情更加清楚明白。另外,对于一个static的方法而言,无法访问泛型类的类型参数。

public <T> void f(T x){
    System.out.println(x.getClass().getName());
}

调用:

gm.f("");
gm.f(1);
gm.f(1.0);
...

使用泛型方法的时候,通常不必指明参数类型,因为编译器会为我们找出具体的类型。这称为类型参数判断。这就好像是f()被无限次的重载过。如果f()调用时传入了基本类型,那么自动打包机制就会接入其中,将基本类型的值包装为对应的对象。

  • 15.4.3 用于Generator的泛型方法
public class Generators {
    public static <T> Collection<T> fill(Collection<T> coll, Generator<T> generator, int n){
        for(int i =0; i < n; i++)
            coll.add(generator.next());
        return coll;
    }
}

//调用
fill(new ArrayList<Coffee>(), new CoffeeGenerator(), 4);
  • 15.5 匿名内部类
public static Generator<Teller> generator = 
   new Generator<Teller>(){
       public Teller next(){ return new Teller(); }
   };
  • 15.7 擦除的神秘之处
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1==c2);

输出为true。

Java泛型是使用擦除来实现的,在泛型代码内部,无法获得任何有关泛型参数类型的信息。因此List和List在运行时事实上是相同的类型。

因为泛型内部是没有类型信息的,所以要调用t.f()方法时,也是不可以的,解决方法是给定泛型类的边界:,如此,就可以调用t.f().

public Test<T extends HasF>{
    private T t;
    ...
    public void test(){ t.f(); }
}

编译器会把类型参数替换为它的擦除,上述的例子T擦除到了HasF.

如果自己去执行擦除,那么前一个例子可以简单的创建出一个没有泛型的类:

public Test(){
    private HasF t;
    ...
    public void test(){ t.f(); }
}

但是泛型可以返回确切的类型,而用Object的话必须强转:

public T get() { return t; }
  • 15.7.2 迁移兼容性

泛型类型只有在静态类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为它们的非泛型上届,例如,List这样的类型注解将被擦除为List,而普通的类型变量在未指定边界的情况下将被擦除为Object。

迁移兼容性:擦除的核心动机是它使得泛化的客户端可以用非泛化的类库来使用,反之亦然。

  • 15.8 擦除的补偿

擦除丢失了在泛型代码中执行某些操作的能力,任何在运行时需要知道确切类型信息的操作都将无法工作:

T var = new T();//Error
T[] arr = new T[size];//Error
if(arg instanceof T){}//Error
T[] arr = (T)new Object[size];//Unchecked warning
  • 15.8.1 创建类型实例

C++中可以直接创建,Java中的解决方法是传递一个工厂对象,并用它创建新的实例,最便利的工厂对象就是Class对象:

class ClassAsFactory<T>{
    T x;
    public ClssAsFactory(Class<T> kind){
        try{
            x = kind.newInstance();
        }catch(Exception e){
            ...
        }
    }
}

...

ClassAsFactory<Coffee> fe = 
    new ClassAsFactory<>(Coffee.class);

但是如果传入的是Integer.class,可以编译但是最终却catch到了异常,因为Integer没有任何默认的构造器。因此Sun建议使用显示的工厂。

interface Factory<T>{
    T create();
}

class Foo2<T>{
    private T x;
    public <F extends Factory<T>> Foo2(F factory){
        x = factory.create();
    }
}

class IntegerFactory implements Factory<Integer>{
    public Integer create(){
        return new Integer(0);
    }
}

//调用
new Foo2<Integer>(new IntegerFactory());

或者是模板方法设计模式,下面的示例中,create()就是在子类中定义的、用来产生子类类型的对象:

abstract class GenericWithCreate<T>{
    final T element;
    GenericWithCreate(){ element = create(); }
    abstract T create();
}

class X {}

class Creator extends GenericWithCreate<X>{
    X create() { return new X(); }
    void f(){
        ...
    }
}

//调用
Creator c = new Creator();
c.f();
  • 15.8.2 泛型数组
    不能直接创建泛型数组,一般的解决方案是在任何想要创建泛型数组的地方都使用ArrayList:
private List<T> array = new ArrayList<T>();
public void add(T item) {}
public T get() {}

既然所有数组无论它们持有的类型如何,都具有相同的结构(每个数组槽位的尺寸和数组的布局),那么看起来应该能创建一个Object数组,并将其转型为所希望的数组类型,事实上这可以编译,但是不能运行:

public class ArrayOfGeneric {
    static final int SIZE = 100;
    static Generic<Integer>[] gia;
    static Generic[] giaNormal;
    public static void main(String[] args){
        //java.lang.Object; cannot be cast to [Lcom.whu.fly.Chapter15.GenericInterface.Generic;
//        gia = (Generic<Integer>[]) new Object[SIZE];
        //Error:创建泛型数组
//        gia = new Generic<Integer>[SIZE];
        gia = new Generic[SIZE];
        System.out.println(gia.getClass().getSimpleName());
        giaNormal = new Generic[SIZE];
        giaNormal[0] = new Generic<Double>();
        gia[0] = new Generic<Integer>();

    }
}

数组将跟踪它们的实际类型,而这个类型是在数组被创建的时候确定的,因此,即使gia已经被转型为Generic[],但这个信息只存在于编译器,在运行时,它仍旧是Object数组。

gia = (Generic[]) new Object[SIZE]将报java.lang.Object; cannot be cast to [Lcom.whu.fly.Chapter15.GenericInterface.Generic的错误。而gia = new Generic[SIZE]将报Error:创建泛型数组的错误。

第一个错误原因显而易见,第二个错误原因==我觉得==是无法建立泛型数组,所以用了通用的Generic[]类型,但是gia中并不能放其他类型的,gia[1] = new Generic()会报错无法将Double插入到Integer中。

成功创建泛型数组的唯一方式就是创建一个被擦除类型的新数组,然后对其转型:

public class GenericArray<T> {
    private T[] array;
    public GenericArray(int size){
        array = (T[]) new Object[size];
    }
    public void put(int index, T item){
        array[index] = item;
    }
    public T get(int index){
        return array[index];
    }
    public T[] rep(){
        return array;
    }
    public static void main(String[] args){
        GenericArray<Integer> gai = new GenericArray<>(10);
//        java.lang.Object; cannot be cast to [Ljava.lang.Integer
//        Integer[] ia = gai.rep();

        Object[] oa = gai.rep();
    }
}

rep()返回的是T[],但是尝试左右Integer[]引用来捕获,依然会报错,这是因为实际的运行时类型是Object[]。

因为有了擦除,数组的运行时类型就只能是Object[],如果我们立即将其转型为T[],那么在编译器该数组的实际类型就将丢失,而编译器可能会错误某些潜在的错误检查。正因为这样,最好是在集合内部使用Object[]:

public class GenericArray2<T> {
    private Object[] array;
    public GenericArray2(int size){
        array = new Object[size];
    }
    public void put(int index, T item){
        array[index] = item;
    }
    public T get(int index){
        return (T)array[index];
    }
    public T[] rep(){
        return (T[])array;
    }
    public static void main(String[] args){
        GenericArray2<Integer> gai = new GenericArray2<>(10);
        for(int i = 0; i < 10; i ++)
            gai.put(i, i);
        for(int i = 0; i < 10; i++)
            System.out.print(gai.get(i) + " ");
        System.out.println();
        //java.lang.Object; cannot be cast to [Ljava.lang.Integer
//        Integer[] ia = gai.rep();

    }
}

依然不能将gai.rep()转为Integer[],它只能是Object[]。

如果确实需要具体的类型,可以传递一个类型标记:

public class GenericArrayWithTypeToken<T> {
    private T[] array;
    public GenericArrayWithTypeToken(Class<T> type, int size){
        array = (T[]) Array.newInstance(type, size);
    }
    public void put(int index, T item){
        array[index] = item;
    }
    public T get(int index){
        return array[index];
    }
    public T[] rep(){
        return array;
    }
    public static void main(String[] args){
        GenericArrayWithTypeToken<Integer> gai = new GenericArrayWithTypeToken<>(Integer.class, 10);
        Integer[] ia = gai.rep();
    }
}

类型标记Class被传到构造器中,以便从擦除中回复,使得我们可以创建需要的实际类型的数组。
- 15.9 边界

如15.7所讲,边界使得你可以在用于泛型的参数类型上设置限制条件,可以按照自己的边界类型来调用方法。

public class ColoredDimension<T extends FruitClass & Inter1 & Inter2> {
}

边界可以设置多个,但是class必须在前面,然后是接口(接口可以是多个)。
- 15.10 通配符

数组可以由一个父类的引用来持有子类的数组,例如:

Fruit[] fruits = new Apple[10];
fruits[0] = new Apple();
//可以编译,但是不能运行
fruits[1] = new Fruit();

因为它有一个Fruit[]的引用,所以它没有理由不允许将Fruit对象或者任何其子类加入其中,因此,在编译器这是允许的,但是它的实际类型是Apple[],在运行时,数组机制知道它处理的是Apple[],因此会抛出异常。

在使用泛型容器时,这种“向上转型”就不允许了:

//编译期报错
List<Fruit> fruitList = new ArrayList<Apple>();

这实际上根本不是向上转型,Apple的List不是Fruit的List。

真正的问题是我们在谈论容器的类型,而不是容器持有的类型。与数组不同,泛型没有内建的协变类型(????)。这是因为数组在语言中是完全定义的,因此可以内建了编译期和运行时的检查,但是在使用泛型时,编译期和运行时系统都不知道你想用类型做些什么,以及应采用什么样的规则。

有时你想要在两个类型之间建立某种类型的向上转型关系,这正是通配符所允许的:

List<? extends Fruit> fList = new ArrayList<Apple>();

但是此时就丢失了向其中传递任何对象的能力,甚至是Object:

fList.add(new Apple());//Error
fList.add(new Fruit());//Error
fList.add(new Object());//Error

fList.add(null);//可以,但是没有意义

List

public class Holder<T>{
    private T value;
    public Holder(){

    }
    public Holder(T val){
        value = val;
    }
    public void set(T val){
        value = val;
    }
    public T get(){
        return value;
    }
}

Holder<? extends Fruit> fruit = new Holder<Apple>(new Apple());

创建了一个Holder,可以将其向上转型为Holder

static void writeTo(List<? super Apple> apples){
    apples.add(new Apple());
    apples.add(new Jonathan());
    apples.add(new Fruit());//Error
}
  • 15.10.3 无界通配符

无界通配符

ArrayList fList = new ArrayList<Fruit>();
fList.add(new Apple("an apple"));
fList.add(new Fruit());
fList.add(new Object());
//fList.add(null);
for(Object f : fList){
    System.out.println(f.getClass().getSimpleName());
}
Apple apple = (Apple) fList.get(0);
System.out.println(apple.getAppleName());

完全可以正常运行,可以添加,可以强制类型转换后(get()返回的是Object)调用它的方法。

ArrayList<Fruit> fList = new ArrayList();

可以是任何,限制跟普通的new ArrayList一样。

public class Wildcards {
    static void rawArgs(Holder holder, Object arg){
//        holder.set(arg);//unchecked warning
//        holder.set(new Wildcards());//same warning

        //OK, 但是类型信息丢失了
        Object obj = holder.get();
    }

    static void unboundedArg(Holder<?> holder, Object arg){
//        holder.set(arg);//Error.Capture of ? cannot be applied to objec
//        holder.set(new Wildcards());//same error

        //OK, 但是类型信息丢失了
        Object object = holder.get();
    }


    static <T> T exact1(Holder<T> holder){
        T t = holder.get();
        return t;
    }

    static <T> T exact2(Holder<T> holder, T arg){
        holder.set(arg);
        T t = holder.get();
        return t;
    }

    static <T> T wildSubtype(Holder<? extends T> holder, T arg){
//        holder.set(arg);//Error capture of ? extends T cannot be applied to T

        T t = holder.get();
        return t;
    }

    static <T> void wildSupertype(Holder<? super T> holder, T arg){
        holder.set(arg);
//        T t= holder.get();//? super T 不是T

        Object obj = holder.get();
    }

    public static void main(String[] args){
        Holder raw = new Holder<Long>();
        //Or
        raw = new Holder();
        Holder<Long> qualified = new Holder<>();
        Holder<?> unbounded = new Holder<Long>();
        Holder<? extends Long> bounded = new Holder<Long>();
        Long lng = 1L;

        rawArgs(raw, lng);
        rawArgs(qualified, lng);
        rawArgs(unbounded, lng);
        rawArgs(bounded, lng);

        unboundedArg(raw, lng);
        unboundedArg(qualified, lng);
        unboundedArg(unbounded, lng);
        unboundedArg(bounded, lng);

        Object ri = exact1(raw);//unchecked warning
        Long r2 = exact1(qualified);
        Object r3 = exact1(unbounded);//Must return Object
        Long r4 = exact1(bounded);

        Long r5 = exact2(raw, lng);//unchecked warning,from Holder to Holder<Long>
        Long r6 = exact2(qualified, lng);
//        Long r7 = exact2(unbounded, lng);//Error, (Holder<T>, T) cannot be applied to (Holder<capture of ?>, Long)
//        Long r8 = exact2(bounded, lng);//Error, (Holder<T>, T) cannot be applied to (Holder<capture of ? extends Long>, Long)

        Long r9 = wildSubtype(raw, lng);//unchecked warning
        Long r10 = wildSubtype(qualified, lng);
        //OK, 但是只能返回Object
        Object r11 = wildSubtype(unbounded, lng);
        Long r12 = wildSubtype(bounded, lng);

        wildSubtype(raw, lng);//unchecked warning,from Holder to Holder<Long>
        wildSupertype(qualified, lng);
//        wildSupertype(unbounded, lng);//Error:(Holder<? super T>, T>) cannot be applied to (Holder<capture of ?>, Long);
//        wildSupertype(bounded, lng);//Error:(Holder<? super T>, T>) cannot be applied to (Holder<capture of ? extends Long>, Long);
    }
}

上面的示例中包含了各种Holder作为参数的用法,它们都具有不同的形式,包括原生类型,具体的类型参数以及无界通配符参数。

总结如下:
1. 只要使用了原生类型,都会放弃编译期的检查,在rawArgs()中,可以将任何类型的对象传递给set(),这个对象会被向上转型为Object。
2. 在unboundedArg()中可以看出

public class CaptureConversion {
    static <T> void f1(Holder<T> holder){
        T t = holder.get();
        System.out.println(t.getClass().getSimpleName());
    }

    static void f2(Holder<?> holder){
        f1(holder);
    }
    public static void main(String[] args){
        Holder raw = new Holder<Integer>(1);
        f1(raw);//有警告
        f2(raw);//无警告
        Holder rawBasic = new Holder();
        rawBasic.set(new Object());//警告
        f2(rawBasic);//无警告
        Holder<?> wildcard = new Holder<>(1.0);
        f2(wildcard);
    }
}

output//
Integer
Integer
Object
Double

最后一个是根据(1.0)判断的,如果换成了1.0f,那么就会输出Float。

此处,f1()中的类型参数都是确切的,没有通配符或者边界。在f2()中,Holder参数是一个无界通配符,因此它看起来是未知的。但是,在f2()中,f1()被调用,而f1()需要一个已知参数。这里所发生的的是:参数类型在调用f2()的过程中被捕获。

  • 15.11.2 实现参数化接口

一个类不能实现同一个泛型接口的两种变体,由于擦除的原因,这两个变体会成为相同的接口:

interface Payable<t>{}

class Employee implements Payable<Employee> {}

//不能编译
class Hourly extends Employee implements Payable<Hourly> {}

如果从Payable的两种用法中都移除掉泛型参数,这段代码就可以编译。

  • 15.11.4 重载
public class UseList<W,T>{
    void f(List<T> t){}
    void f(List<W> w){}
}

由于擦除的原因,重载方法将产生相同的类型签名,因此必须提供明显有区别的方法名,f1(),f2()。

  • 15.12 自限定的类型
class SelfBounded<T extends SelfBounded<T>>{}

基类用导出类替代其参数,这意味着泛型基类变成了一种其所有导出类的公共的模板。

class A extends SelfBounded<A> {}
class B extends SelfBounded<A> {}//也行
class C extends SelfBounded<B> {}//Error,B不满足T extends SelfBound<T>(T一致)

自限定的参数的意义:它可以保证类型参数必须与正在被定义的类相同,即这个类所用的类型参数将与使用这个参数的类具有相同的基类型。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java中泛型 的相关文章

  • Java 中的 XPath 节点集

    我在 eclipse 中有这段代码 NodeSet nodes NodeSet xPath evaluate expression inputSource XPathConstants NODESET 它给我 NodeSet 上的编译时错误
  • 如果测试用例失败,Selenium Web 驱动程序无法关闭 Firefox 实例

    我各位 我正在使用 junit 和 selenium web 驱动程序 2 28 问题是 如果我运行成功的测试用例 Web 驱动器能够关闭 Firefox 实例 但是当测试用例失败时 Selenium Web 驱动器无法关闭 Firefox
  • 如何在 JFace 的 TableViewer 中创建复选框?

    我创建了一个包含两列的 tableViewer 我想将其中一列设为复选框 为此 我创建了一个 CheckBoxCellEditor 但我不知道为什么它不起作用 名为 tableName 的列显示其值正常 色谱柱规格如下 String COL
  • 如何在一行中将字符串数组转换为双精度数组

    我有一个字符串数组 String guaranteedOutput Arrays copyOf values values length String class 所有字符串值都是数字 数据应转换为Double QuestionJava 中
  • 如何测试 JUnit 测试的 Comparator?

    我需要测试 Compare 方法 但我对如何测试感到困惑 我可以看看该怎么做吗 public class MemberComparator implements Comparator
  • 线程自动利用多个CPU核心?

    假设我的应用程序运行 2 个线程 例如渲染线程和游戏更新线程 如果它在具有多核 CPU 当今典型 的移动设备上运行 我是否可以期望线程在可能的情况下自动分配给不同的核心 我知道底层操作系统内核 Android linux内核 决定调度 我的
  • 解决错误:日志已在具有多个实例的atomikos中使用

    我仅在使用atomikos的实时服务器上遇到问题 在我的本地服务器上它工作得很好 我在服务器上面临的问题是 init 中出错 日志已在使用中 完整的异常堆栈跟踪 java lang RuntimeException Log already
  • manifest.mf 文件的附加内容的约定?

    Java JAR 中的 MANIFEST MF 文件是否有任何超出 MANIFEST MF 约定的约定 JAR规范 http download oracle com javase 1 4 2 docs guide jar jar html
  • 在数据流模板中调用 waitUntilFinish() 后可以运行代码吗?

    我有一个批处理 Apache Beam 作业 它从 GCS 获取文件作为输入 我的目标是根据执行后管道的状态将文件移动到两个 GCS 存储桶之一 如果管道执行成功 则将文件移动到存储桶 A 否则 如果管道在执行过程中出现任何未处理的异常 则
  • Java 页面爬行和解析之 Crawler4j 与 Jsoup

    我想获取页面的内容并提取其中的特定部分 据我所知 此类任务至少有两种解决方案 爬虫4j https github com yasserg crawler4j and Jsoup http jsoup org 它们都能够检索页面的内容并提取其
  • 使用替换字符串中多个单词的最有效方法[重复]

    这个问题在这里已经有答案了 此刻我正在做 Example line replaceAll replaceAll cat dog replaceAll football rugby 我觉得那很丑 不确定有更好的方法吗 也许循环遍历哈希图 ED
  • OnClick 事件中的 finish() 如何工作?

    我有一个Activity一键退出Activity 通过layout xml我必须设置OnClick事件至cmd exit调用 this finish 效果很好 public void cmd exit View editLayout thi
  • Prim 的迷宫生成算法:获取相邻单元格

    我基于 Prim 算法编写了一个迷宫生成器程序 该算法是 Prim 算法的随机版本 从充满墙壁的网格开始 选择一个单元格 将其标记为迷宫的一部分 将单元格的墙壁添加到墙壁列表中 While there are walls in the li
  • 序列化对象以进行单元测试

    假设在单元测试中我需要一个对象 其中所有 50 个字段都设置了一些值 我不想手动设置所有这些字段 因为这需要时间而且很烦人 不知何故 我需要获得一个实例 其中所有字段都由一些非空值初始化 我有一个想法 如果我要调试一些代码 在某个时候我会得
  • 在具有相同属性名称的不同数据类型上使用 ModelMapper

    我有两节课说Animal AnimalDto我想用ModelMapper将 Entity 转换为 DTO 反之亦然 但是对于具有相似名称的一些属性 这些类应该具有不同的数据类型 我该如何实现这一目标 动物 java public class
  • Spring Data 与 Spring Data JPA 与 JdbcTemplate

    我有信心Spring Data and Spring Data JPA指的是相同的 但后来我在 youtube 上观看了一个关于他正在使用JdbcTemplate在那篇教程中 所以我在那里感到困惑 我想澄清一下两者之间有什么区别Spring
  • 检查 protobuf 消息 - 如何按名称获取字段值?

    我似乎无法找到一种方法来验证 protobuf 消息中字段的值 而无需显式调用其 getter 我看到周围的例子使用Descriptors FieldDescriptor实例到达消息映射内部 但它们要么基于迭代器 要么由字段号驱动 一旦我有
  • 将多模块 Maven 项目导入 Eclipse 时出现问题 (STS 2.5.2)

    我刚刚花了最后一个小时查看 Stackoverflow com 上的线程 尝试将 Maven 项目导入到 Spring ToolSuite 2 5 2 中 Maven 项目有多个模块 当我使用 STS 中的 Import 向导导入项目时 所
  • Android JNI C 简单追加函数

    我想制作一个简单的函数 返回两个字符串的值 基本上 java public native String getAppendedString String name c jstring Java com example hellojni He
  • 如何修复“sessionFactory”或“hibernateTemplate”是必需的问题

    我正在使用 Spring Boot JPA WEB 和 MYSQL 创建我的 Web 应用程序 它总是说 sessionFactory or hibernateTemplate是必需的 我该如何修复它 我已经尝试过的东西 删除了本地 Mav

随机推荐

  • public Map kaoYanAllStation() { Map map = new HashMap<>(); ...

    首先 根据代码中的注释可以看出 该方法主要是获取各种气象数据 对其进行计算和比较 然后将结果存储在一个 Map 对象中返回 为了优化这段代码 可以考虑以下几个方面 减少重复代码 在代码中可以看到 获取历年同期降水和温度数据的代码几乎一模一样
  • LVS常用模式(DR、NAT、TUN)以及ldirector和keepalived

    1 LVS简单介绍 1 lvs定义LVS是Linux Virtual Server的简写 意即Linux虚拟服务器 是一个虚拟的服务器集群系统 LVS集群采用IP负载均衡技术和基于内容请求分发技术 调度器具有很好的吞吐率 将请求均衡地转移到
  • Java学习教程,Java从入门到精通,全套Java视频教程+笔记+配套工具

    目录 一 大纲 一 Java基础 二 计算机基础 三 工具的使用 四 数据库 五 web前端 六 JavaWeb 七 框架 八 互联网分布式技术 发现身边很多自学java却放弃的 真的挺可惜的 白白浪费了几个月宝贵的时间 且放弃一次 就会有
  • 第二十二章 Spring AOP⾥⾯的代理知识

    1 静态代理和动态代理 什么是代理 为某 个对象创建 个代理对象 程序不直接 原本的对象 是由创建的代理对象来控制对原对象 通过代理类这中间 层 能有效控制对委托类对象的直接访问 也可以很好地隐藏和保护委托类对象 同时也为实施不同控制策略预
  • 05-网络的四层协议和七层协议

    TCP IP网络分层模型 TCP IP的设计创造性的提出了分层的概念 把复杂的网络通信划分出多个层次 再为每一个层次分配不同的职责 层次内只专心做好自己的事情 用分而治之的思想把一个大麻烦拆分成了数个小麻烦 从而解决了网络的难题 TCP I
  • JAVA中的for循环使用方法

    一 循环结构 1 概念 在学习Java里的循环之前 我们先来了解一下到底什么是循环 以及循环的作用 我们先来看下面这张图 大家想一下 我们在400米的跑道上参加万米长跑 正常情况下要跑25圈 这25圈每一圈的跑步过程其实都是一样的 相当于是
  • springboot过滤器和拦截器

    一 过滤器和拦截器的区别 1 过滤器和拦截器触发时机不一样 过滤器是在请求进入容器后 但请求进入servlet之前进行预处理的 请求结束返回也是 是在servlet处理完后 返回给前端之前 2 拦截器可以获取IOC容器中的各个bean 而过
  • Volatility3内存取证工具使用详解

    Volatility 介绍 Volatility是一款开源的内存取证分析工具 是一款开源内存取证框架 能够对导出的内存镜像进行分析 通过获取内核数据结构 使用插件获取内存的详细情况以及系统的运行状态 支持Windows Linux MaC
  • org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder 找不到依赖包

    org springframework boot autoconfigure jdbc DataSourceBuilder 找不到依赖包 org springframework boot autoconfigure jdbc DataSou
  • PowerOJ2546: fork【C++ STL __gnu_cxx::rope】

    题目链接 我们可以这样定义一个可持久化数组 rope
  • MIPI TX控制器的设计

    MIPI接口在移动设备中被广泛应用 主要用于传输图像传感器 液晶显示器等外设的数据 以MIPI DPHY v1 2为例 它包含一个CLK lane和若干个DATA lane 可配置 每个lane的最高速率可达到2 5Gbps 对比SerDe
  • 向win7旗舰版U盘启动盘 添加usb3.0driver

    以前的主板usb采用的是ehci controller 仅支持usb2 0 而现在的主板一般采用xhci controller 同时支持usb2 0和usb3 0 win7的镜像安装包里面的驱动并没有xhci的驱动 所以在如今的很多新平台的
  • 怎么样对阿里云ECS主机进行绑定域名

    首先我有个阿里的 域名 虚拟云主机 搭建了一个wordpress 的网站 地址为 www liuxun name wordpress liuxun name wordpress 现在我想把一个阿里云的ECS主机 里面装了tomcat 希望以
  • GET请求里的body问题

    故事还得从一个bug说起 今天有人问我 为什么发到后端的请求400了 我说肯定是参数不对 你去检查检查GET POST之类的方法写没写对 要么就是字段没对上 无非是这几个问题 然后他说检查过了 没问题啊 我不太相信 但是看了看前端发送的请求
  • springmvc 03(JSR303和拦截器)

    目录 一 JSR303 1 服务端验证 2 步骤 二 拦截器 1 简介 2 拦截器与过滤器 2 1 什么是过滤器 2 2 拦截器和过滤器的区别 3 拦截器案例 3 1 使用原理 一 JSR303 1 服务端验证 2 步骤 1 导入pom x
  • 【数据结构】6.5 红黑树(C++)

    数据结构 6 5 红黑树 没有学过二叉搜索树 也叫二叉排序树或二叉查找树 的小伙伴们建议先学习一下 这样阅读会更轻松哦 点我学习二叉搜索树 目录 一 红黑树的概念和性质 二 红黑树的存储结构和声明 三 红黑树的构建过程 四 红黑树的实现 1
  • Windows7上使用VS2013编译Caffe源码(不带GPU支持)步骤

    1 从https github com BVLC caffe 通过git clone下载caffe源码 master分支 版本号为09868ac git clone https github com BVLC caffe git 2 先使用
  • Visio画出简单的拓扑图

    1 选择类别 类别 网络 基本网络图 2 画图 在左边选择 模具 上方选择 连接线
  • Ubuntu下编译并运行C++代码

    安装完Ubuntu后 用户目录有时候也叫 home 文件夹或者主文件夹 它的路径是 home username 其中 username 就是我们登录 Linux 时使用的用户名 Linux 会在 home 目录下为每一个登录的用户创建一个文
  • Java中泛型

    Thinking in Java 第15章笔记 即使使用了接口 就要求代码必须使用特定的接口 对程序的约束也还是太强了 我们希望达到的目的是编写更通用的代码 要使代码能够应用与 某种不具体的类型 而不是一个具体的接口或类 泛型这个术语的意思