一、泛型概要

泛型(Generic)的本质是类型参数化,通俗的说就是用一个占位符来表示类型,这个类型可以是String,Integer等不确定的类型,表明可接受的类型。

泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用。

1.1、为什么需要泛型

在数据结构中有一种结构叫:栈,它的特点是:先进后出,后进先出

如:放衣服的箱子,糖葫芦

现在来模拟一个栈的数据结构

1.1.1、版本一(强类型)

  1. package com.nf.math;
  2.  
  3. public class ObjUtil {
  4. public static void main(String[] args) {
  5. Stack stack=new Stack();
  6. stack.push(11);
  7. stack.push(22);
  8. stack.push(55);
  9. int data=stack.pop();
  10. System.out.println(data);
  11. System.out.println(stack.pop());
  12. System.out.println(stack.pop());
  13. }
  14. }
  15.  
  16. class Stack{
  17. //用于存放数据的数组
  18. private int[] data=new int[10];
  19. //当前下标
  20. private int i=0;
  21. //进栈
  22. public void push(int obj){
  23. data[i++]=obj;
  24. }
  25. //出栈
  26. public int pop(){
  27. return data[--i];
  28. }
  29. }

结果:

  1. 55
  2. 22
  3. 11

缺点是不通用

1.1.2、版本二(Object弱类型)

版本一有明显的缺点,只允许存放int类型的数据,如果需要其它类型的数据怎么办法?

有人提议重新创建不同类型的栈,这样不好,因为如果需要10种不同类型的栈,则需定义10个,维护也麻烦。

使用Object也许可以解决问题,代码如下:

  1. package com.nf.math;
  2.  
  3. public class ObjUtil {
  4. public static void main(String[] args) {
  5. Stack stack=new Stack();
  6. stack.push(1.1);
  7. stack.push(2.2);
  8. stack.push(5.5);
  9. double data=(double)stack.pop();
  10. System.out.println(data);
  11. System.out.println(stack.pop());
  12. System.out.println(stack.pop());
  13. }
  14. }
  15.  
  16. class Stack{
  17. //用于存放数据的数组
  18. private Object[] data=new Object[10];
  19. //当前下标
  20. private int i=0;
  21. //进栈
  22. public void push(Object obj){
  23. data[i++]=obj;
  24. }
  25. //出栈
  26. public Object pop(){
  27. return data[--i];
  28. }
  29. }

结果:

  1. 5.5
  2. 2.2
  3. 1.1

缺点是安全隐患(类型转换)

1.1.3、版本三(泛型)

版本二中存在类型的强制转换,如果转换的类型不匹配则会引起运行时异常,存在安全隐患,使用泛型可以解决该问题:

  1. package com.nf.math;
  2.  
  3. public class ObjUtil {
  4. public static void main(String[] args) {
  5. Stack<Double> stack=new Stack<Double>();
  6. stack.push(1.1);
  7. stack.push(2.2);
  8. stack.push(5.5);
  9. double data=stack.pop(); //不需要拆箱,没有类型转换
  10. System.out.println(data);
  11. System.out.println(stack.pop());
  12. System.out.println(stack.pop());
  13. }
  14. }
  15.  
  16. class Stack<T>{
  17. //用于存放数据的数组
  18. private T[] data=(T[])(new Object[10]);
  19. //当前下标
  20. private int i=0;
  21. //进栈
  22. public void push(T obj){
  23. data[i++]=obj;
  24. }
  25. //出栈
  26. public T pop(){
  27. return data[--i];
  28. }
  29. }

结果:

  1. 5.5
  2. 2.2
  3. 1.1

因为使用了泛型,兼具了版本一与版本二的优点,没有类型转换,没有安全隐患,可以适用多种不同的数据类型。

java不支持泛型数组,List或ArrayList具有泛型数组的功能。

1.2、泛型的优点

没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。

泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。

1、提高程序的安全性和可靠性
使用类型更安全,指定具体类型后,Java编译器会对错误的类型在编译时被捕获,而不是在运行时当作ClassCastException展示出来,从而提高程序的安全性和可靠性

2、消除强制类型转换
例如在集合里使用泛型后,从集合里取出对象后就不需要再进行强制类型转换了,这样使编写程序变得更简单,更不容易出错

3、提高代码重用率
在一个类里要对不同结构类型的对象进行操作时,有的对象成员和方法的逻辑都是一样的,就是类型不一样,就有可能会造成不必要的代码重复,通过使用泛型,只需要一个Java类就可以表示不同类型的对象,从而可以大大提高代码的重用率

1.3、泛型规则

1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。

  1. Stack<Double> stack=new Stack<Double>();

2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。

  1. Stack<Integer> stack2=stack; //错误

3、泛型的类型参数可以有多个。

  1. class Stack<T,E,K,M,X>{
  2. }

4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”。

泛型约束,约束T的类型只能是superclass的子类型

5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");

1.4、Java中方法的参数

java中方法参数传递的都是值,与C#区别很大,没有ref与out。

java中数据类型分为基本数据类型和引用数据类型。

  • 基本数据类型

    • 整型:byte,short,int,long
    • 浮点型:float,double
    • 字符型:char
    • 布尔型:boolean
  • 引用数据类型 
    • 数组
    • 接口

方法的参数分为实际参数,和形式参数。

  • 形式参数:定义方法时写的参数。
  • 实际参数:调用方法时写的具体数值。

1.4.1、基本数据类型

  1. package com.nf.math;
  2.  
  3. public class ObjUtil {
  4. public static void main(String[] args) {
  5. Util util=new Util();
  6. int n1=100,n2=200;
  7. util.Swap(n1, n2); //副本
  8. System.out.println("n1="+n1+",n2="+n2);
  9. }
  10. }
  11.  
  12. class Util
  13. {
  14. public void Swap(int n1, int n2) {
  15. int temp = n1;
  16. n1 = n2;
  17. n2 = temp;
  18. }
  19. }

结果:

  1. n1=100,n2=200

从上面的结果可以看出n1与n2在调用交换方法后并没有实质交换,是因为形参是n1与n2的副本。

1.4.2、引用类型

  1. package com.nf.math;
  2.  
  3. public class ObjUtil {
  4. public static void main(String[] args) {
  5. Util util=new Util();
  6. String n1="100",n2="200";
  7. util.Swap(n1, n2); //形参是n1的引用副本
  8. System.out.println("n1="+n1+",n2="+n2);
  9. }
  10. }
  11.  
  12. class Util
  13. {
  14. public void Swap(String n1, String n2) {
  15. String temp = n1;
  16. n1 = n2;
  17. n2 = temp;
  18. }
  19. }

结果:

  1. n1=100,n2=200

依然没有交互

String对象做为参数传递时,走的依然是引用传递,只不过String这个类比较特殊。

String对象一旦创建,内容不可更改。每一次内容的更改都是重现创建出来的新对象。

1.4.3、结论

  • 值传递的时候,将实参的,copy一份给形参。
  • 引用传递的时候,将实参的地址值,copy一份给形参。

也就是说,不管是值传递还是引用传递,形参拿到的仅仅是实参的副本,而不是实参本身。

二、自定义泛型类

示例:

  1. package com.nf.math;
  2.  
  3. public class BoxTest {
  4. public static void main(String[] args) {
  5. Box<Integer> boxInt = new Box<Integer>();
  6. boxInt.setAttr(5);
  7. System.out.println(boxInt.getAttr() + 1);
  8. }
  9. }
  10.  
  11. class Box<T> {
  12. private T attr;
  13.  
  14. public T getAttr() {
  15. return this.attr;
  16. }
  17.  
  18. public void setAttr(T attr) {
  19. this.attr = attr;
  20. }
  21. }

结果:

6

三、自定义泛型方法

在自定义泛型类中,整个类都可以使用类型占位T,有时候只需要局部用到则可以定义泛型方法

定义泛型方法,语法如下:

  1. [访问修饰符] <泛型列表> 返回值 方法名(参数列表) {
  2. //方法体;
  3. }

示例:

  1. package com.nf.math;
  2.  
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Modifier;
  5.  
  6. public class BoxTest {
  7. public static void main(String[] args) {
  8. Box<Integer> boxInt = new Box<Integer>();
  9. boxInt.setAttr(5);
  10. System.out.println(boxInt.getAttr() + 1);
  11.  
  12. ReflectUtil.getMethods(Box.class);
  13.  
  14. }
  15. }
  16.  
  17. class Box<T> {
  18. private T attr;
  19.  
  20. public T getAttr() {
  21. return this.attr;
  22. }
  23.  
  24. public void setAttr(T attr) {
  25. this.attr = attr;
  26. }
  27. }
  28.  
  29. class ReflectUtil
  30. {
  31. public static <T> void getMethods(Class<T> type){
  32. Method[] methods=type.getMethods();
  33. System.out.println(type.getName()+":");
  34. for (Method method : methods) {
  35. System.out.println(Modifier.toString(method.getModifiers())+" "+method.getReturnType()+" "+method.getName()+"()");
  36. }
  37. }
  38. }

结果:

说明:

1)在泛型列表中声明的泛型,可用于该方法的返回值类型声明、参数类型声明和方法代码中的局部变量的类型声明

2)类中其他方法不能使用当前方法声明的泛型

3)使用泛型方法时,不必指明参数类型,编译器会自己找出具体的类型;泛型方法除了定义不同,调用就像普通方法一样。

注意:是否拥有泛型方法,与其所在的类是否泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前。

四、通配符与泛型约束

4.1、类型通配符

类型通配符一般是使用 ? 代替具体的类型实参。

  1. package com.nf.math;
  2.  
  3. public class BeanTest {
  4. public static void main(String[] args) {
  5. // 同一种泛型可以对应多个版本(因为参数类型是不确定的)
  6. // 不同版本的泛型类实例是不兼容的。
  7. Bean<String> bean1 = new Bean<String>();
  8. Bean<Integer> bean2 = new Bean<Integer>();
  9. System.out.println(bean1.getClass().getName()); // 类型
  10. System.out.println(bean2.getClass().getName());
  11. Bean<Number> bean3 = bean1;
  12. }
  13. }
  14.  
  15. class Bean<T> {
  16. private T var;
  17.  
  18. public T getVar() {
  19. return var;
  20. }
  21.  
  22. public void setVar(T var) {
  23. this.var = var;
  24. }
  25.  
  26. @Override
  27. public String toString() {
  28. return var.toString();
  29. }
  30. }

运行结果:

示例

  1. package com.nf.math;
  2.  
  3. public class BeanTest {
  4. public static void main(String[] args) {
  5. // 同一种泛型可以对应多个版本(因为参数类型是不确定的)
  6. // 不同版本的泛型类实例是不兼容的。
  7. Bean<String> bean1 = new Bean<String>();
  8. bean1.setVar("one");
  9. Bean<Integer> bean2 = new Bean<Integer>();
  10. bean2.setVar(2);
  11. System.out.println(bean1.getClass().getName()); // 类型
  12. System.out.println(bean2.getClass().getName());
  13. Bean<?> bean3 = bean1;
  14.  
  15. Show(bean1);
  16. //Show(bean2);错误
  17. Display(bean1);
  18. Display(bean2);
  19. }
  20.  
  21. public static void Show(Bean<String> p){
  22. System.out.println("内容是:"+p.getVar());
  23. }
  24. public static void Display(Bean<?> p){
  25. System.out.println("内容是:"+p);
  26. }
  27. }
  28.  
  29. class Bean<T> {
  30. private T var;
  31.  
  32. public T getVar() {
  33. return var;
  34. }
  35.  
  36. public void setVar(T var) {
  37. this.var = var;
  38. }
  39.  
  40. @Override
  41. public String toString() {
  42. return var.toString();
  43. }
  44. }

结果:

  1. com.nf.math.Bean
  2. com.nf.math.Bean
  3. 内容是:one
  4. 内容是:one
  5. 内容是:2

示例:

  1. class Info<T>{
  2. private T var ; // 定义泛型变量
  3. public void setVar(T var){
  4. this.var = var ;
  5. }
  6. public T getVar(){
  7. return this.var ;
  8. }
  9. public String toString(){ // 直接打印
  10. return this.var.toString() ;
  11. }
  12. };
  13. public class GenericsDemo{
  14. public static void main(String args[]){
  15. Info<String> i = new Info<String>() ; // 使用String为泛型类型
  16. i.setVar("it") ; // 设置内容
  17. fun(i) ;
  18. }
  19. public static void fun(Info<?> temp){ // 可以接收任意的泛型对象
  20. System.out.println("内容:" + temp) ;
  21. }
  22. };

4.2、 上界

4.2.1、通配符上界

类型通配符上限通过形如Stack<? extends Number>形式定义

  1. class Info<T>{
  2. private T var ; // 定义泛型变量
  3. public void setVar(T var){
  4. this.var = var ;
  5. }
  6. public T getVar(){
  7. return this.var ;
  8. }
  9. public String toString(){ // 直接打印
  10. return this.var.toString() ;
  11. }
  12. };
  13. public class GenericsDemo17{
  14. public static void main(String args[]){
  15. Info<Integer> i1 = new Info<Integer>() ; // 声明Integer的泛型对象
  16. Info<Float> i2 = new Info<Float>() ; // 声明Float的泛型对象
  17. i1.setVar(30) ; // 设置整数,自动装箱
  18. i2.setVar(30.1f) ; // 设置小数,自动装箱
  19. fun(i1) ;
  20. fun(i2) ;
  21. }
  22. public static void fun(Info<? extends Number> temp){ // 只能接收Number及其Number的子类
  23. System.out.print(temp + "、") ;
  24. }
  25. };

示例

  1. package com.nf.math;
  2.  
  3. public class BeanTest {
  4. public static void main(String[] args) {
  5. Bean<String> bean1 = new Bean<String>();
  6. bean1.setVar("one");
  7. //public final class Integer extends Number implements Comparable<Integer> {
  8. Bean<Integer> bean2 = new Bean<Integer>();
  9. bean2.setVar(2);
  10. Bean<Number> bean3 = new Bean<Number>();
  11. bean3.setVar(3);
  12.  
  13. //Display(bean1); 错误,原因是T必须继承Number或就是Number类型
  14. Display(bean2);
  15. Display(bean3);
  16. }
  17.  
  18. public static void Display(Bean<? extends Number> p){
  19. System.out.println("内容是:"+p);
  20. }
  21. }
  22.  
  23. class Bean<T> {
  24. private T var;
  25.  
  26. public T getVar() {
  27. return var;
  28. }
  29.  
  30. public void setVar(T var) {
  31. this.var = var;
  32. }
  33.  
  34. @Override
  35. public String toString() {
  36. return var.toString();
  37. }
  38. }

结果

  1. 内容是:2
  2. 内容是:3

4.2.2、占位符上界

  1. package com.nf.math;
  2.  
  3. public class BeanTest {
  4. public static void main(String[] args) {
  5. Bean<String> bean1 = new Bean<String>(); //错误,因为T有上界,要求是Number或Number的子类
  6. //public final class Integer extends Number implements Comparable<Integer>
  7. Bean<Integer> bean2 = new Bean<Integer>();
  8. Bean<Number> bean3 = new Bean<Number>();
  9. }
  10. }
  11.  
  12. class Bean<T extends Number> { //T必须继承Number或就是Number
  13. private T var;
  14.  
  15. public T getVar() {
  16. return var;
  17. }
  18.  
  19. public void setVar(T var) {
  20. this.var = var;
  21. }
  22.  
  23. @Override
  24. public String toString() {
  25. return var.toString();
  26. }
  27. }

4.2.3、多种限制

  1. class CT extends Comparable<? super T & Serializable

我们来分析以下这句,T extends Comparable这个是对上限的限制,Comparable< super T>这个是下限的限制,Serializable是第2个上限。一个指定的类型参数可以具有一个或多个上限。具有多重限制的类型参数可以用于访问它的每个限制的方法和域。

  1. class Bean<T extends Number & Serializable>

4.3、下界

4.3.1、通配符下界

类型通配符下限为Stack<? super Number>形式,其含义与类型通配符上限正好相反

  1. class Info<T>{
  2. private T var ; // 定义泛型变量
  3. public void setVar(T var){
  4. this.var = var ;
  5. }
  6. public T getVar(){
  7. return this.var ;
  8. }
  9. public String toString(){ // 直接打印
  10. return this.var.toString() ;
  11. }
  12. };
  13. public class GenericsDemo21{
  14. public static void main(String args[]){
  15. Info<String> i1 = new Info<String>() ; // 声明String的泛型对象
  16. Info<Object> i2 = new Info<Object>() ; // 声明Object的泛型对象
  17. i1.setVar("hello") ;
  18. i2.setVar(new Object()) ;
  19. fun(i1) ;
  20. fun(i2) ;
  21. }
  22. public static void fun(Info<? super String> temp){ // 只能接收String或Object类型的泛型
  23. System.out.print(temp + "、") ;
  24. }
  25. };

示例:

  1. package com.nf.math;
  2.  
  3. import java.io.Serializable;
  4.  
  5. public class BeanTest {
  6. public static void main(String[] args) {
  7. Bean<String> bean1 = new Bean<String>();
  8. bean1.setVar("one");
  9. //public abstract class Number implements java.io.Serializable
  10. Bean<Integer> bean2 = new Bean<Integer>();
  11. bean2.setVar(2);
  12. Bean<Number> bean3 = new Bean<Number>();
  13. bean3.setVar(3);
  14. Bean<Object> bean4 = new Bean<Object>();
  15. bean4.setVar(4);
  16. Bean<Serializable> bean5 = new Bean<Serializable>();
  17. bean5.setVar("5");
  18.  
  19. //Display(bean1); //错误
  20. //Display(bean2); //错误
  21. Display(bean3);
  22. Display(bean4);
  23. Display(bean5);
  24. }
  25.  
  26. //?必是Number或Number的父类,Object,Serializable,Number
  27. public static void Display(Bean<? super Number> p){
  28. System.out.println("内容是:"+p);
  29. }
  30. }
  31.  
  32. class Bean<T> {
  33. private T var;
  34.  
  35. public T getVar() {
  36. return var;
  37. }
  38.  
  39. public void setVar(T var) {
  40. this.var = var;
  41. }
  42.  
  43. @Override
  44. public String toString() {
  45. return var.toString();
  46. }
  47. }

结果:

  1. 内容是:3
  2. 内容是:4
  3. 内容是:5

4.3.2、占位符下界

没有,不存在...

五、类型擦除

5.1、类型擦除

Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List<Object>和List<String>等类型,在编译之后都会变成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法避免在运行时刻出现类型转换异常的情况。类型擦除也是Java的泛型实现方式与C++模板机制实现方式之间的重要区别。

Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类型在编译后都会被清除掉。

Java在泛型设计上是一种“伪泛型”,存在着泛型擦除。

类型擦除是Java中泛型的实现方式。泛型是在编译器这个层次来实现的。在Java 源代码中声明的泛型类型信息,在编译过程中会被擦除,只保留不带类型参数的形式。被擦除的类型信息包括泛型类型和泛型方法声明时的形式类型参数,以及参数化类型中的实际类型信息。经过类型擦除之后,包含泛型类型的代码被转换成不包含泛型类型的代码,相当于回到了泛型被引入之前的形式,Java虚拟机在运行字节代码时并不知道泛型类型的存在。

定义好的泛型类:

  1. public class ObjectHolder<T> {
  2. private T obj;
  3. public T getObject() {
  4. return obj;
  5. }
  6. public void setObject(T obj) {
  7. this.obj = obj;
  8. }
  9. }

被转译后:

以上面代码中的ObjectHolder泛型类为例,经过类型擦除后,由于形式类型参数T没有上界,T的所有出现将被替换成Object类型

  1. public class ObjectHolder {
  2. private Object obj;
  3. public Object getObject() {
  4. return obj;
  5. }
  6. public void setObject(Object obj) {
  7. this.obj = obj;
  8. }
  9. }

另外使用ObjectHolder类的代码也要进行处理,如下列代码所示:

  1. ObjectHolder<String> holder = new ObjectHolder<String>();
  2. holder.setObject("Hello");
  3. String str = holder.getObject();

在类型擦除后,ObjectHolder类中的getObject方法的返冋值类型实际上是 Object类型,因此需要添加强制类型转换把getObject方法的返回值转换成String类型。 这些类型转换操作由编译器自动添加。由于编译器已经确保不允许使用除String类的对象之外的其他对象调用setObject方法,因此这个强制类型转换操作始终是合法的,如下列代码所示:

  1. ObjectHolder holder = new ObjectHolder();
  2. holder.setObject("Hello");
  3. String str = (String)holder.getObject();

泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除

示例:

  1. package com.nf.math;
  2.  
  3. public class BeanTest {
  4. public static void main(String[] args) {
  5. Bean<Integer> bean1=new Bean<Integer>();
  6. Bean<String> bean2=new Bean<String>();
  7.  
  8. System.out.println(bean1.getClass().getName());
  9. System.out.println(bean2.getClass().getName());
  10.  
  11. }
  12. }
  13.  
  14. class Bean<T> {
  15. private T var;
  16.  
  17. public T getVar() {
  18. return var;
  19. }
  20.  
  21. public void setVar(T var) {
  22. this.var = var;
  23. }
  24.  
  25. @Override
  26. public String toString() {
  27. return var.toString();
  28. }
  29. }

结果:

  1. com.nf.math.Bean
  2. com.nf.math.Bean 

在JVM中的 Class 都是com.nf.math.Bean,泛型信息被擦除了。

5.2、泛型转译

示例:

  1. package com.nf.math;
  2.  
  3. import java.lang.reflect.Field;
  4.  
  5. public class BeanTest {
  6. public static void main(String[] args) throws Exception {
  7. Bean<Integer> bean1=new Bean<Integer>();
  8. Bean<String> bean2=new Bean<String>();
  9.  
  10. //获得Bean中的字段var
  11. Field clazz=bean1.getClass().getDeclaredField("var");
  12. clazz.setAccessible(true);
  13. clazz.set(bean1, "abc");
  14. //取字段var的类型
  15. System.out.println(clazz.getType().getSimpleName());
  16. System.out.println(bean1.getVar());
  17.  
  18. }
  19. }
  20.  
  21. class Bean<T> {
  22. private T var;
  23.  
  24. public T getVar() {
  25. return var;
  26. }
  27.  
  28. public void setVar(T var) {
  29. this.var = var;
  30. }
  31.  
  32. @Override
  33. public String toString() {
  34. return var.toString();
  35. }
  36. }

因为Bean没有上界,T被转译成Object,生成如下代码:

  1. class BeanCopy {
  2. private Object var;
  3.  
  4. public Object getVar() {
  5. return var;
  6. }
  7.  
  8. public void setVar(Object var) {
  9. this.var = var;
  10. }
  11.  
  12. @Override
  13. public String toString() {
  14. return var.toString();
  15. }
  16. } 

运行结果:

  1. Object
  2. abc

泛型类被类型擦除后,相应的类型就被替换成 Object 类型呢?,不一定,如果有上界,则被替换成上界:

  1. package com.nf.math;
  2.  
  3. import java.lang.reflect.Field;
  4.  
  5. public class BeanTest {
  6. public static void main(String[] args) throws Exception {
  7. Bean<Integer> bean1=new Bean<Integer>();
  8. Bean<Number> bean2=new Bean<Number>();
  9.  
  10. //获得Bean中的字段var
  11. Field clazz=bean1.getClass().getDeclaredField("var");
  12. //查看运行时字段var的类型
  13. System.out.println(clazz.getType().getSimpleName());
  14. }
  15. }
  16.  
  17. class Bean<T extends Number> { //设置上界
  18. private T var;
  19.  
  20. public T getVar() {
  21. return var;
  22. }
  23.  
  24. public void setVar(T var) {
  25. this.var = var;
  26. }
  27.  
  28. @Override
  29. public String toString() {
  30. return var.toString();
  31. }
  32. }

运行结果:

  1. Number

类型被转译成:

  1. class Bean{
  2. private Number var;
  3.  
  4. public Number getVar() {
  5. return var;
  6. }
  7.  
  8. public void setVar(Number var) {
  9. this.var = var;
  10. }
  11.  
  12. @Override
  13. public String toString() {
  14. return var.toString();
  15. }
  16. }

六、泛型应用

6.1、泛型与反射简化JDBCUtils工具类示例

参考:https://commons.apache.org/proper/commons-dbutils/

已经写好的工具类:

  1. package com.zhangguo.utils;
  2.  
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.lang.reflect.Method;
  5. import java.sql.Connection;
  6. import java.sql.DriverManager;
  7. import java.sql.PreparedStatement;
  8. import java.sql.ResultSet;
  9. import java.sql.ResultSetMetaData;
  10. import java.sql.SQLException;
  11. import java.sql.Statement;
  12. import java.util.ArrayList;
  13. import java.util.HashMap;
  14. import java.util.List;
  15. import java.util.Map;
  16.  
  17. public class JDBCUtils {
  18.  
  19. public static String DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
  20. public static String URL = "jdbc:sqlserver://localhost:1433;databasename=pubs";
  21. public static String USER_NAME = "sa";
  22. public static String PASSWORD = "sa";
  23.  
  24. static {
  25. try {
  26. Class.forName(DRIVER);
  27. } catch (ClassNotFoundException e) {
  28. e.printStackTrace();
  29. }
  30. }
  31.  
  32. private JDBCUtils() {
  33.  
  34. }
  35.  
  36. /**
  37. * Get connection
  38. *
  39. * @return
  40. */
  41. public static Connection getconnnection() {
  42. Connection con = null;
  43. try {
  44. con = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
  45. } catch (SQLException e) {
  46. e.printStackTrace();
  47. }
  48. return con;
  49. }
  50.  
  51. /**
  52. * Close connection
  53. *
  54. * @param rs
  55. * @param st
  56. * @param con
  57. */
  58. public static void close(ResultSet rs, Statement st, Connection con) {
  59. try {
  60. try {
  61. if (rs != null) {
  62. rs.close();
  63. }
  64. } finally {
  65. try {
  66. if (st != null) {
  67. st.close();
  68. }
  69. } finally {
  70. if (con != null)
  71. con.close();
  72. }
  73. }
  74. } catch (SQLException e) {
  75. e.printStackTrace();
  76. }
  77. }
  78.  
  79. /**
  80. * Close connection
  81. *
  82. * @param rs
  83. */
  84. public static void close(ResultSet rs) {
  85. Statement st = null;
  86. Connection con = null;
  87. try {
  88. try {
  89. if (rs != null) {
  90. st = rs.getStatement();
  91. rs.close();
  92. }
  93. } finally {
  94. try {
  95. if (st != null) {
  96. con = st.getConnection();
  97. st.close();
  98. }
  99. } finally {
  100. if (con != null) {
  101. con.close();
  102. }
  103. }
  104. }
  105. } catch (SQLException e) {
  106. e.printStackTrace();
  107. }
  108. }
  109.  
  110. /**
  111. * Close connection
  112. *
  113. * @param st
  114. * @param con
  115. */
  116. public static void close(Statement st, Connection con) {
  117. try {
  118. try {
  119. if (st != null) {
  120. st.close();
  121. }
  122. } finally {
  123. if (con != null)
  124. con.close();
  125. }
  126. } catch (SQLException e) {
  127. e.printStackTrace();
  128. }
  129. }
  130.  
  131. /**
  132. * insert/update/delete
  133. *
  134. * @param sql
  135. * @param args
  136. * @return
  137. */
  138. public static int update(String sql, Object... args) {
  139. int result = 0;
  140. Connection con = getconnnection();
  141. PreparedStatement ps = null;
  142. try {
  143. ps = con.prepareStatement(sql);
  144. if (args != null) {
  145. for (int i = 0; i < args.length; i++) {
  146. ps.setObject((i + 1), args[i]);
  147. }
  148. }
  149. result = ps.executeUpdate();
  150. } catch (SQLException e) {
  151. e.printStackTrace();
  152. } finally {
  153. close(ps, con);
  154. }
  155.  
  156. return result;
  157. }
  158.  
  159. /**
  160. * query, because need to manually close the resource, so not recommended
  161. * for use it
  162. *
  163. * @param sql
  164. * @param args
  165. * @return ResultSet
  166. */
  167. @Deprecated
  168. public static ResultSet query(String sql, Object... args) {
  169. ResultSet result = null;
  170. Connection con = getconnnection();
  171. PreparedStatement ps = null;
  172. try {
  173. ps = con.prepareStatement(sql);
  174. if (args != null) {
  175. for (int i = 0; i < args.length; i++) {
  176. ps.setObject((i + 1), args[i]);
  177. }
  178. }
  179. result = ps.executeQuery();
  180. } catch (SQLException e) {
  181. e.printStackTrace();
  182. }
  183. return result;
  184. }
  185.  
  186. /**
  187. * Query a single record
  188. *
  189. * @param sql
  190. * @param args
  191. * @return Map<String,Object>
  192. */
  193. public static Map<String, Object> queryForMap(String sql, Object... args) {
  194. Map<String, Object> result = new HashMap<String, Object>();
  195. List<Map<String, Object>> list = queryForList(sql, args);
  196. if (list.size() > 0) {
  197. result = list.get(0);
  198. }
  199. return result;
  200. }
  201.  
  202. /**
  203. * Query a single record
  204. *
  205. * @param sql
  206. * @param args
  207. * @return <T>
  208. */
  209. public static <T> T queryForObject(String sql, Class<T> clz, Object... args) {
  210. T result = null;
  211. List<T> list = queryForList(sql, clz, args);
  212. if (list.size() > 0) {
  213. result = list.get(0);
  214. }
  215. return result;
  216. }
  217.  
  218. /**
  219. * Query a single record
  220. *
  221. * @param sql
  222. * @param args
  223. * @return List<Map<String,Object>>
  224. */
  225. public static List<Map<String, Object>> queryForList(String sql, Object... args) {
  226. List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
  227. Connection con = null;
  228. ResultSet rs = null;
  229. PreparedStatement ps = null;
  230. try {
  231. con = getconnnection();
  232. ps = con.prepareStatement(sql);
  233. if (args != null) {
  234. for (int i = 0; i < args.length; i++) {
  235. ps.setObject((i + 1), args[i]);
  236. }
  237. }
  238. rs = ps.executeQuery();
  239. ResultSetMetaData rsmd = rs.getMetaData();
  240. int columnCount = rsmd.getColumnCount();
  241. while (rs.next()) {
  242. Map<String, Object> map = new HashMap<String, Object>();
  243. for (int i = 1; i <= columnCount; i++) {
  244. map.put(rsmd.getColumnLabel(i), rs.getObject(i));
  245. }
  246. result.add(map);
  247. }
  248. } catch (SQLException e) {
  249. e.printStackTrace();
  250. } finally {
  251. close(rs, ps, con);
  252. }
  253. return result;
  254. }
  255.  
  256. /**
  257. * Query a single record
  258. *
  259. * @param sql
  260. * @param args
  261. * @return List<T>
  262. */
  263. public static <T> List<T> queryForList(String sql, Class<T> clz, Object... args) {
  264. List<T> result = new ArrayList<T>();
  265. Connection con = null;
  266. PreparedStatement ps = null;
  267. ResultSet rs = null;
  268. try {
  269. con = getconnnection();
  270. ps = con.prepareStatement(sql);
  271. if (args != null) {
  272. for (int i = 0; i < args.length; i++) {
  273. ps.setObject((i + 1), args[i]);
  274. }
  275. }
  276. rs = ps.executeQuery();
  277. ResultSetMetaData rsmd = rs.getMetaData();
  278. int columnCount = rsmd.getColumnCount();
  279. while (rs.next()) {
  280. T obj = clz.newInstance();
  281. for (int i = 1; i <= columnCount; i++) {
  282. String columnName = rsmd.getColumnName(i);
  283. String methodName = "set" + columnName.substring(0, 1).toUpperCase()
  284. + columnName.substring(1, columnName.length());
  285. Method method[] = clz.getMethods();
  286. for (Method meth : method) {
  287. if (methodName.equals(meth.getName())) {
  288. meth.invoke(obj, rs.getObject(i));
  289. }
  290. }
  291. }
  292. result.add(obj);
  293. }
  294. } catch (InstantiationException e) {
  295. e.printStackTrace();
  296. } catch (IllegalAccessException e) {
  297. e.printStackTrace();
  298. } catch (SQLException e) {
  299. e.printStackTrace();
  300. } catch (IllegalArgumentException e) {
  301. e.printStackTrace();
  302. } catch (InvocationTargetException e) {
  303. e.printStackTrace();
  304. } finally {
  305. close(rs, ps, con);
  306. }
  307. return result;
  308. }
  309. }

数据库:

  1. CREATE TABLE `student` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
  3. `name` varchar(50) DEFAULT NULL COMMENT '姓名',
  4. `sex` varchar(20) DEFAULT NULL,
  5. `cno` varchar(50) DEFAULT NULL COMMENT '班级',
  6. `addr` varchar(50) DEFAULT NULL COMMENT '籍贯',
  7. PRIMARY KEY (`id`)
  8. ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;

示例:

  1. package com.zhangguo.utils;
  2.  
  3. import java.util.List;
  4.  
  5. public class JDBCUtilsTest {
  6. public static void main(String[] args) {
  7. List<Student> students = JDBCUtils.queryForList("select * from student where id<=?", Student.class, 10);
  8. for (Student student : students) {
  9. System.out.println(student);
  10. }
  11. }
  12. }
  13.  
  14. /** 学生 */
  15. class Student {
  16. private int id;
  17. private String name;
  18. private String sex;
  19. private String cno;
  20. private String addr;
  21.  
  22. public int getId() {
  23. return id;
  24. }
  25.  
  26. public void setId(int id) {
  27. this.id = id;
  28. }
  29.  
  30. public String getName() {
  31. return name;
  32. }
  33.  
  34. public void setName(String name) {
  35. this.name = name;
  36. }
  37.  
  38. public String getSex() {
  39. return sex;
  40. }
  41.  
  42. public void setSex(String sex) {
  43. this.sex = sex;
  44. }
  45.  
  46. public String getCno() {
  47. return cno;
  48. }
  49.  
  50. public void setCno(String cno) {
  51. this.cno = cno;
  52. }
  53.  
  54. public String getAddr() {
  55. return addr;
  56. }
  57.  
  58. public void setAddr(String addr) {
  59. this.addr = addr;
  60. }
  61.  
  62. @Override
  63. public String toString() {
  64. return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", cno=" + cno + ", addr=" + addr + "]";
  65. }
  66. }

结果:

6.2、实现泛型与反射简化JDBCUtils工具类

数据库与表的元数据就是用于描述数据库或表的信息,如表的字段(长度,类型,注释),主键,外键,约束等信息,表中的数据本来是描述客观事物的。

6.2.1、获得表的元信息方法一

示例:

  1. /**
  2. * 根据数据库的连接参数,获取指定表的基本信息:字段名、字段类型、字段注释
  3. * 表名
  4. * @return Map集合
  5. */
  6. public static List getTableInfo(String table) {
  7. List result = new ArrayList();
  8.  
  9. Connection conn = null;
  10. DatabaseMetaData dbmd = null;
  11.  
  12. try {
  13. conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
  14.  
  15. dbmd = conn.getMetaData();
  16. ResultSet resultSet = dbmd.getTables(null, "%", table, new String[] { "TABLE" });
  17.  
  18. while (resultSet.next()) {
  19. String tableName = resultSet.getString("TABLE_NAME");
  20. System.out.println(tableName);
  21.  
  22. if (tableName.equals(table)) {
  23. ResultSet rs = conn.getMetaData().getColumns(null, getSchema(conn), tableName.toUpperCase(), "%");
  24.  
  25. while (rs.next()) {
  26. // System.out.println("字段名:"+rs.getString("COLUMN_NAME")+"--字段注释:"+rs.getString("REMARKS")+"--字段数据类型:"+rs.getString("TYPE_NAME"));
  27. Map map = new HashMap();
  28. String colName = rs.getString("COLUMN_NAME");
  29. map.put("code", colName);
  30.  
  31. String remarks = rs.getString("REMARKS");
  32. if (remarks == null || remarks.equals("")) {
  33. remarks = colName;
  34. }
  35. map.put("name", remarks);
  36.  
  37. String dbType = rs.getString("TYPE_NAME");
  38. map.put("dbType", dbType);
  39.  
  40. map.put("valueType", changeDbType(dbType));
  41. result.add(map);
  42. }
  43. }
  44. }
  45. } catch (SQLException e) {
  46. e.printStackTrace();
  47. } catch (Exception e) {
  48. e.printStackTrace();
  49. } finally {
  50. try {
  51. conn.close();
  52. } catch (SQLException e) {
  53. e.printStackTrace();
  54. }
  55. }
  56.  
  57. return result;
  58. }
  59.  
  60. private static String changeDbType(String dbType) {
  61. dbType = dbType.toUpperCase();
  62. switch (dbType) {
  63. case "VARCHAR":
  64. case "VARCHAR2":
  65. case "CHAR":
  66. return "1";
  67. case "NUMBER":
  68. case "DECIMAL":
  69. return "4";
  70. case "INT":
  71. case "SMALLINT":
  72. case "INTEGER":
  73. return "2";
  74. case "BIGINT":
  75. return "6";
  76. case "DATETIME":
  77. case "TIMESTAMP":
  78. case "DATE":
  79. return "7";
  80. default:
  81. return "1";
  82. }
  83. }
  84.  
  85. // 其他数据库不需要这个方法 oracle和db2需要
  86. private static String getSchema(Connection conn) throws Exception {
  87. String schema;
  88. schema = conn.getMetaData().getUserName();
  89. if ((schema == null) || (schema.length() == 0)) {
  90. throw new Exception("ORACLE数据库模式不允许为空");
  91. }
  92. return schema.toUpperCase().toString();
  93. }

结果:

  1. persons
  2. student
  3. [{code=id, valueType=2, name=编号, dbType=INT}, {code=name, valueType=1, name=姓名, dbType=VARCHAR}, {code=sex, valueType=1, name=sex, dbType=VARCHAR}, {code=cno, valueType=1, name=班级, dbType=VARCHAR}, {code=addr, valueType=1, name=籍贯, dbType=VARCHAR}]

6.2.2、获得表的元信息方法二

代码:

  1. package com.zhangguo.utils;
  2.  
  3. import java.sql.Connection;
  4. import java.sql.DriverManager;
  5. import java.sql.PreparedStatement;
  6. import java.sql.ResultSet;
  7. import java.sql.ResultSetMetaData;
  8. import java.sql.SQLException;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11.  
  12. public class DbUtils {
  13. public static String DRIVER = "com.mysql.jdbc.Driver";
  14. public static String URL = "jdbc:mysql://localhost:3306/studentmis?useUnicode=true&characterEncoding=utf8";
  15. public static String USER_NAME = "mysqluser";
  16. public static String PASSWORD = "root";
  17.  
  18. static {
  19. try {
  20. Class.forName(DRIVER);
  21. } catch (ClassNotFoundException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25.  
  26. public static List<Student> getStudents(String sql, Object... params) throws Exception {
  27. List<Student> result = new ArrayList<>();
  28.  
  29. Connection conn = null;
  30. PreparedStatement pstm = null;
  31.  
  32. conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
  33. pstm = conn.prepareStatement(sql);
  34. // 添加参数
  35. if (params != null) {
  36. for (int i = 0; i < params.length; i++) {
  37. pstm.setObject(i + 1, params[i]);
  38. }
  39. }
  40. // 执行查询
  41. ResultSet rs = pstm.executeQuery();
  42.  
  43. //获得表的元数据
  44. ResultSetMetaData rsmd=pstm.getMetaData();
  45. //表的列数
  46. int metaSize=rsmd.getColumnCount();
  47. for (int i =1; i <=metaSize; i++) {
  48. System.out.println("列名:"+rsmd.getColumnName(i));
  49. System.out.println("长度:"+rsmd.getColumnDisplaySize(i));
  50. System.out.println("类型:"+rsmd.getColumnType(i));
  51. System.out.println("类型:"+rsmd.getColumnTypeName(i));
  52. System.out.println("----------");
  53. }
  54.  
  55. while (rs.next()) {
  56. Student student=new Student();
  57.  
  58. student.setAddr(rs.getString("addr"));
  59. student.setCno(rs.getString("cno"));
  60. student.setId(rs.getInt("id"));
  61. student.setName(rs.getString("name"));
  62. student.setSex(rs.getString("sex"));
  63.  
  64. result.add(student);
  65. }
  66.  
  67. CloseConnection(conn, pstm, rs);
  68. return result;
  69. }
  70.  
  71. public static void CloseConnection(Connection conn, PreparedStatement pstm, ResultSet rs) throws SQLException {
  72. if (rs != null) {
  73. rs.close();
  74. }
  75. if (pstm != null) {
  76. pstm.close();
  77. }
  78. if (conn != null) {
  79. conn.close();
  80. }
  81. }
  82.  
  83. }

测试类:

  1. package com.zhangguo.utils;
  2.  
  3. import java.util.List;
  4.  
  5. public class JDBCUtilsTest {
  6. public static void main(String[] args) throws Exception {
  7. List<Student> students = JDBCUtils.queryForList("select * from student where id<=?", Student.class, 10);
  8. for (Student student : students) {
  9. System.out.println(student);
  10. }
  11. System.out.println("-------------------------------------------------------------------");
  12. List<Student> stus =DbUtils.getStudents("select * from student where id<=?", 5);
  13. for (Student student : stus) {
  14. System.out.println(student);
  15. }
  16. System.out.println("-------------------------------------------------------------------");
  17. System.out.println(stus);
  18. }
  19. }
  20.  
  21. /** 学生 */
  22. class Student {
  23. private int id;
  24. private String name;
  25. private String sex;
  26. private String cno;
  27. private String addr;
  28.  
  29. public int getId() {
  30. return id;
  31. }
  32.  
  33. public void setId(int id) {
  34. this.id = id;
  35. }
  36.  
  37. public String getName() {
  38. return name;
  39. }
  40.  
  41. public void setName(String name) {
  42. this.name = name;
  43. }
  44.  
  45. public String getSex() {
  46. return sex;
  47. }
  48.  
  49. public void setSex(String sex) {
  50. this.sex = sex;
  51. }
  52.  
  53. public String getCno() {
  54. return cno;
  55. }
  56.  
  57. public void setCno(String cno) {
  58. this.cno = cno;
  59. }
  60.  
  61. public String getAddr() {
  62. return addr;
  63. }
  64.  
  65. public void setAddr(String addr) {
  66. this.addr = addr;
  67. }
  68.  
  69. @Override
  70. public String toString() {
  71. return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", cno=" + cno + ", addr=" + addr + "]";
  72. }
  73. }

结果:

  1. Student [id=1, name=张学友, sex=男, cno=S1SJ90, addr=中国香港]
  2. Student [id=2, name=黎明, sex=男, cno=S1SJ18, addr=中国上海]
  3. Student [id=3, name=张慧妹, sex=女, cno=S2SJ19, addr=中国北京]
  4. Student [id=4, name=张国立, sex=男, cno=S3J37, addr=中国广东]
  5. Student [id=5, name=张哪啦, sex=女, cno=S3SR96, addr=中国杭州]
  6. Student [id=6, name=张铁林, sex=男, cno=S2SJ140, addr=中国珠海]
  7. Student [id=7, name=张国荣, sex=男, cno=S1SJ111, addr=中国香港]
  8. Student [id=8, name=张果果, sex=女, cno=S3SU198, addr=中国深圳]
  9. Student [id=10, name=张小军, sex=女, cno=S1SN196, addr=中国斗门]
  10. -------------------------------------------------------------------
  11. 列名:id
  12. 长度:11
  13. 类型:4
  14. 类型:INT
  15. ----------
  16. 列名:name
  17. 长度:50
  18. 类型:12
  19. 类型:VARCHAR
  20. ----------
  21. 列名:sex
  22. 长度:20
  23. 类型:12
  24. 类型:VARCHAR
  25. ----------
  26. 列名:cno
  27. 长度:50
  28. 类型:12
  29. 类型:VARCHAR
  30. ----------
  31. 列名:addr
  32. 长度:50
  33. 类型:12
  34. 类型:VARCHAR
  35. ----------
  36. Student [id=1, name=张学友, sex=男, cno=S1SJ90, addr=中国香港]
  37. Student [id=2, name=黎明, sex=男, cno=S1SJ18, addr=中国上海]
  38. Student [id=3, name=张慧妹, sex=女, cno=S2SJ19, addr=中国北京]
  39. Student [id=4, name=张国立, sex=男, cno=S3J37, addr=中国广东]
  40. Student [id=5, name=张哪啦, sex=女, cno=S3SR96, addr=中国杭州]
  41. -------------------------------------------------------------------
  42. [Student [id=1, name=张学友, sex=男, cno=S1SJ90, addr=中国香港], Student [id=2, name=黎明, sex=男, cno=S1SJ18, addr=中国上海], Student [id=3, name=张慧妹, sex=女, cno=S2SJ19, addr=中国北京], Student [id=4, name=张国立, sex=男, cno=S3J37, addr=中国广东], Student [id=5, name=张哪啦, sex=女, cno=S3SR96, addr=中国杭州]]

6.3、封装DbUtilis工具类

6.3.1.、查询功能

  1. package com.zhangguo.utilities;
  2.  
  3. import java.lang.reflect.Field;
  4. import java.sql.Connection;
  5. import java.sql.DriverManager;
  6. import java.sql.PreparedStatement;
  7. import java.sql.ResultSet;
  8. import java.sql.ResultSetMetaData;
  9. import java.sql.SQLException;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12.  
  13. public class DbUtils {
  14. public static String DRIVER = "com.mysql.jdbc.Driver";
  15. public static String URL = "jdbc:mysql://localhost:3306/studentmis?useUnicode=true&characterEncoding=utf8";
  16. public static String USER_NAME = "mysqluser";
  17. public static String PASSWORD = "root";
  18.  
  19. static {
  20. try {
  21. Class.forName(DRIVER);
  22. } catch (ClassNotFoundException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26.  
  27. public static <T> List<T> queryList(String sql,Class<T> clazz,Object... params) throws Exception {
  28. List<T> result = new ArrayList<>();
  29.  
  30. Connection conn = null;
  31. PreparedStatement pstm = null;
  32.  
  33. conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
  34. pstm = conn.prepareStatement(sql);
  35. // 添加参数
  36. if (params != null) {
  37. for (int i = ; i < params.length; i++) {
  38. pstm.setObject(i + , params[i]);
  39. }
  40. }
  41. // 执行查询
  42. ResultSet rs = pstm.executeQuery();
  43.  
  44. // 获得表的元数据
  45. ResultSetMetaData rsmd = pstm.getMetaData();
  46. // 表的列数
  47. int metaSize = rsmd.getColumnCount();
  48.  
  49. while (rs.next()) {
  50. T entity = clazz.newInstance();
  51.  
  52. for (int i = ; i <= metaSize; i++) {
  53. //获得当前列的列名
  54. String columnName=rsmd.getColumnName(i);
  55. //根据字段名获得实体类中的字段
  56. Field field=clazz.getDeclaredField(columnName);
  57. //设置字段可以被访问
  58. field.setAccessible(true);
  59. //给实体中的字段赋值
  60. field.set(entity, rs.getObject(columnName));
  61. }
  62. result.add(entity);
  63. }
  64.  
  65. CloseConnection(conn, pstm, rs);
  66. return result;
  67. }
  68.  
  69. public static void CloseConnection(Connection conn, PreparedStatement pstm, ResultSet rs) throws SQLException {
  70. if (rs != null) {
  71. rs.close();
  72. }
  73. if (pstm != null) {
  74. pstm.close();
  75. }
  76. if (conn != null) {
  77. conn.close();
  78. }
  79. }
  80. }

测试:

  1. package com.zhangguo.utilities;
  2.  
  3. import java.util.List;
  4.  
  5. public class DbUtilsTest {
  6.  
  7. public static void main(String[] args) throws Exception {
  8. List<Animal> result = DbUtils.queryList("select * from animal where id<?", Animal.class, 5);
  9. System.out.println(result);
  10. List<Student> students = DbUtils.queryList("select * from student where id<?", Student.class, 5);
  11. System.out.println(students);
  12. }
  13.  
  14. }
  15.  
  16. /** 动物 */
  17. class Animal {
  18. private int id;
  19. private String name;
  20. private String color;
  21.  
  22. public int getId() {
  23. return id;
  24. }
  25.  
  26. public void setId(int id) {
  27. this.id = id;
  28. }
  29.  
  30. public String getName() {
  31. return name;
  32. }
  33.  
  34. public void setName(String name) {
  35. this.name = name;
  36. }
  37.  
  38. public String getColor() {
  39. return color;
  40. }
  41.  
  42. public void setColor(String color) {
  43. this.color = color;
  44. }
  45.  
  46. @Override
  47. public String toString() {
  48. return "Animal [id=" + id + ", name=" + name + ", color=" + color + "]";
  49. }
  50. }
  51.  
  52. /** 学生 */
  53. class Student { //T只允许是String或String的子类
  54. private int id;
  55. private String name;
  56. private String sex;
  57. private String cno;
  58. private String addr;
  59.  
  60. public int getId() {
  61. return id;
  62. }
  63.  
  64. public void setId(int id) {
  65. this.id = id;
  66. }
  67.  
  68. public String getName() {
  69. return name;
  70. }
  71.  
  72. public void setName(String name) {
  73. this.name = name;
  74. }
  75.  
  76. public String getSex() {
  77. return sex;
  78. }
  79.  
  80. public void setSex(String sex) {
  81. this.sex = sex;
  82. }
  83.  
  84. public String getCno() {
  85. return cno;
  86. }
  87.  
  88. public void setCno(String cno) {
  89. this.cno = cno;
  90. }
  91.  
  92. public String getAddr() {
  93. return addr;
  94. }
  95.  
  96. public void setAddr(String addr) {
  97. this.addr = addr;
  98. }
  99.  
  100. @Override
  101. public String toString() {
  102. return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", cno=" + cno + ", addr=" + addr + "]";
  103. }
  104. }

结果:

  1. [Animal [id=1, name=Dog, color=yellow], Animal [id=2, name=Cat, color=White], Animal [id=3, name=Duck, color=Black]]
  2. [Student [id=1, name=张学友, sex=男, cno=S1SJ90, addr=中国香港], Student [id=2, name=黎明, sex=男, cno=S1SJ18, addr=中国上海], Student [id=3, name=张慧妹, sex=女, cno=S2SJ19, addr=中国北京], Student [id=4, name=张国立, sex=男, cno=S3J37, addr=中国广东]]

6.3.2、增删改功能

  1. package com.zhangguo.utilities;
  2.  
  3. import java.lang.reflect.Field;
  4. import java.sql.Connection;
  5. import java.sql.DriverManager;
  6. import java.sql.PreparedStatement;
  7. import java.sql.ResultSet;
  8. import java.sql.ResultSetMetaData;
  9. import java.sql.SQLException;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12.  
  13. public class DbUtils {
  14. public static String DRIVER = "com.mysql.jdbc.Driver";
  15. public static String URL = "jdbc:mysql://localhost:3306/studentmis?useUnicode=true&characterEncoding=utf8";
  16. public static String USER_NAME = "mysqluser";
  17. public static String PASSWORD = "root";
  18.  
  19. static {
  20. try {
  21. Class.forName(DRIVER);
  22. } catch (ClassNotFoundException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26.  
  27. public static <T> List<T> queryList(String sql, Class<T> clazz, Object... params) throws Exception {
  28. List<T> result = new ArrayList<>();
  29.  
  30. Connection conn = null;
  31. PreparedStatement pstm = null;
  32.  
  33. conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
  34. pstm = conn.prepareStatement(sql);
  35. // 添加参数
  36. if (params != null) {
  37. for (int i = 0; i < params.length; i++) {
  38. pstm.setObject(i + 1, params[i]);
  39. }
  40. }
  41. // 执行查询
  42. ResultSet rs = pstm.executeQuery();
  43.  
  44. // 获得表的元数据
  45. ResultSetMetaData rsmd = pstm.getMetaData();
  46. // 表的列数
  47. int metaSize = rsmd.getColumnCount();
  48.  
  49. while (rs.next()) {
  50. T entity = clazz.newInstance();
  51.  
  52. for (int i = 1; i <= metaSize; i++) {
  53. // 获得当前列的列名
  54. String columnName = rsmd.getColumnName(i);
  55. // 根据字段名获得实体类中的字段
  56. Field field = clazz.getDeclaredField(columnName);
  57. // 设置字段可以被访问
  58. field.setAccessible(true);
  59. // 给实体中的字段赋值
  60. field.set(entity, rs.getObject(columnName));
  61. }
  62. result.add(entity);
  63. }
  64.  
  65. CloseConnection(conn, pstm, rs);
  66. return result;
  67. }
  68.  
  69. public static <T> T queryObject(String sql, Class<T> clazz, Object... params) throws Exception {
  70. List<T> result = queryList(sql, clazz, params);
  71. if (result != null && !result.isEmpty()) {
  72. return result.get(0);
  73. }
  74. return null;
  75. }
  76.  
  77. public static int execute(String sql, Object... params) throws Exception {
  78. int rows = 0;
  79. Connection conn = null;
  80. PreparedStatement pstm = null;
  81.  
  82. conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
  83. pstm = conn.prepareStatement(sql);
  84. // 添加参数
  85. if (params != null) {
  86. for (int i = 0; i < params.length; i++) {
  87. pstm.setObject(i + 1, params[i]);
  88. }
  89. }
  90. // 执行
  91. rows = pstm.executeUpdate();
  92. CloseConnection(conn, pstm, null);
  93. return rows;
  94. }
  95.  
  96. public static void CloseConnection(Connection conn, PreparedStatement pstm, ResultSet rs) throws SQLException {
  97. if (rs != null) {
  98. rs.close();
  99. }
  100. if (pstm != null) {
  101. pstm.close();
  102. }
  103. if (conn != null) {
  104. conn.close();
  105. }
  106. }
  107. }

6.4、JSON返回类型封装

  1. package com.zhangguo.utils;
  2.  
  3. import java.util.HashMap;
  4. import java.util.Map;
  5.  
  6. /**
  7. * 返回类型封装
  8. */
  9. public class R extends HashMap<String, Object> {
  10. private static final long serialVersionUID = 1L;
  11.  
  12. public R() {
  13. put("code", 0);
  14. put("msg", "success");
  15. }
  16.  
  17. //错误时
  18. public static R error() {
  19. return error(500, "未知异常,请联系管理员");
  20. }
  21.  
  22. public static R error(String msg) {
  23. return error(500, msg);
  24. }
  25.  
  26. public static R error(int code, String msg) {
  27. R r = new R();
  28. r.put("code", code);
  29. r.put("msg", msg);
  30. return r;
  31. }
  32.  
  33. //成功时
  34. public static R ok(String msg) {
  35. R r = new R();
  36. r.put("msg", msg);
  37. return r;
  38. }
  39.  
  40. public static R ok(Map<String, Object> map) {
  41. R r = new R();
  42. r.putAll(map);
  43. return r;
  44. }
  45.  
  46. public static R ok() {
  47. return new R();
  48. }
  49.  
  50. @Override
  51. public R put(String key, Object value) {
  52. super.put(key, value);
  53. return this;
  54. }
  55. }

测试:

  1. package com.zhangguo.controller;
  2.  
  3. import java.util.*;
  4. import javax.servlet.annotation.WebServlet;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7.  
  8. import com.zhangguo.utils.DbUtils;
  9. import com.zhangguo.utils.JsonUtils;
  10. import com.zhangguo.utils.R;
  11.  
  12. @WebServlet("/StudentController")
  13. public class StudentController extends BaseServlet {
  14. private static final long serialVersionUID = 1L;
  15.  
  16. public void getAllStudent(HttpServletRequest request, HttpServletResponse response) {
  17. List<Student> students=null;
  18. try {
  19. students = DbUtils.queryList("select * from student", Student.class);
  20. String json = JsonUtils.toJson(students);
  21. response.getWriter().append(json);
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. }
  26.  
  27. public void getAllStus(HttpServletRequest request, HttpServletResponse response) {
  28. List<Student> students=null;
  29. try {
  30. students = DbUtils.queryList("select * from student", Student.class);
  31. R r=R.ok().put("data", students).put("time",new Date());
  32. String json = JsonUtils.toJson(r);
  33. response.getWriter().append(json);
  34. } catch (Exception e) {
  35. e.printStackTrace();
  36. }
  37. }
  38.  
  39. }

运行结果:

jsonUtils:

  1. package com.zhangguo.utils;
  2.  
  3. import java.text.SimpleDateFormat;
  4.  
  5. import com.fasterxml.jackson.core.JsonProcessingException;
  6. import com.fasterxml.jackson.databind.ObjectMapper;
  7.  
  8. public class JsonUtils {
  9. /**
  10. * 序列化成json
  11. * */
  12. public static String toJson(Object obj) {
  13. // 对象映射器
  14. ObjectMapper mapper = new ObjectMapper();
  15. SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd HH:mm:ss");
  16. mapper.setDateFormat(sdf);
  17.  
  18. String result = null;
  19. // 序列化user对象为json字符串
  20. try {
  21. result = mapper.writeValueAsString(obj);
  22. } catch (JsonProcessingException e) {
  23. e.printStackTrace();
  24. }
  25. return result;
  26. }
  27.  
  28. /**
  29. * 反序列化成对象
  30. * */
  31. public static <T> T toObject(String json,Class<T> valueType) {
  32. //对象映射器
  33. ObjectMapper mapper=new ObjectMapper();
  34. T result=null;
  35. try {
  36. result=mapper.readValue(json,valueType);
  37.  
  38. }catch (Exception e) {
  39. e.printStackTrace();
  40. }
  41. return result;
  42. }
  43. }

baseServlet:

  1. package com.zhangguo.controller;
  2.  
  3. import java.io.IOException;
  4. import java.lang.reflect.Method;
  5.  
  6. import javax.servlet.ServletConfig;
  7. import javax.servlet.ServletException;
  8. import javax.servlet.annotation.WebServlet;
  9. import javax.servlet.http.HttpServlet;
  10. import javax.servlet.http.HttpServletRequest;
  11. import javax.servlet.http.HttpServletResponse;
  12.  
  13. public class BaseServlet extends HttpServlet {
  14. private static final long serialVersionUID = 1L;
  15.  
  16. public void init(ServletConfig config) throws ServletException {
  17.  
  18. }
  19.  
  20. public String commonObject;
  21. //Write once only once!
  22. protected void service(HttpServletRequest request, HttpServletResponse response)
  23. throws ServletException, IOException {
  24. String action = request.getParameter("act");
  25. request.setCharacterEncoding("utf-8");
  26. response.setCharacterEncoding("utf-8");
  27. response.setContentType("text/html;charset=utf-8");
  28.  
  29. if (action != null) {
  30. try {
  31. // 在当前Servlet实例中根据action找到方法信息
  32. Method method = getClass().getDeclaredMethod(action, HttpServletRequest.class,
  33. HttpServletResponse.class);
  34. if (method != null) {
  35. // 在当前实例上调用方法method,指定参数request,response
  36. method.invoke(this, request, response);
  37. } else {
  38. response.getWriter().write("您请求的action不存在");
  39. }
  40. } catch (Exception e) {
  41. response.getWriter().write("调用发生了错误,错误:" + e.getMessage());
  42. e.printStackTrace();
  43. }
  44.  
  45. } else {
  46. try {
  47. response.getWriter().write("请指定参数act");
  48. } catch (IOException e) {
  49. e.printStackTrace();
  50. }
  51. }
  52. }
  53.  
  54. }

七、视频与示例

https://www.bilibili.com/video/av9219224/

示例下载

八、作业

7.1、请定义一个泛型类,实现简单的List功能,可变长度的数组,可以实现foreach功能。

  1. MyList<Integer> list1=new MyList<Integer>();
  2. list1.add(1);
  3. list1.add(2);
  4. list1.add(3);
  5. foreach(Integer i : list){
  6. System.out.println(i);
  7. }

7.2、请定义一个泛型方法,根据指定的类型获得方法中所有的字段、方法信息。

7.3、请使用JDBC+反射+泛型实现一个可返回强类型的方法,如:

指定SQL语句、参数与类型返回一个强类型对象

  1. List<Student> students = JDBCUtils.queryForList("select * from student where id<=?", Student.class, 10);
  2. for (Student student : students) {
  3. System.out.println(student);
  4. }

7.4、指定一个数据库,生成数据库下所有的表的实体类文件

7.5、JDBC+反射+泛型实现一个简单的ORM(选作)

  1. package model;
  2.  
  3. import java.util.Date;
  4.  
  5. import annotation.Column;
  6. import annotation.Entity;
  7. import annotation.Id;
  8.  
  9. /**
  10. * 图书
  11. */
  12. @Entity("t_book") //表名
  13. public class Book {
  14.  
  15. /**
  16. * 图书编号
  17. */
  18. @Id("t_isbn")
  19. private String isbn;
  20.  
  21. /**
  22. * 书名
  23. */
  24. @Column("t_name")
  25. private String name;
  26.  
  27. /**
  28. * 作者
  29. */
  30. @Column("t_author")
  31. private String author;
  32.  
  33. /**
  34. * 出版社
  35. */
  36. @Column("t_publishing")
  37. private String publishing;
  38.  
  39. /**
  40. * 出版时间
  41. */
  42. @Column(value = "t_pubdate")
  43. private Date pubdate;
  44.  
  45. /**
  46. * 价格
  47. */
  48. @Column(value = "t_price")
  49. private double price;
  50.  
  51. public String getIsbn() {
  52. return isbn;
  53. }
  54.  
  55. public void setIsbn(String isbn) {
  56. this.isbn = isbn;
  57. }
  58.  
  59. public String getName() {
  60. return name;
  61. }
  62.  
  63. public void setName(String name) {
  64. this.name = name;
  65. }
  66.  
  67. public String getAuthor() {
  68. return author;
  69. }
  70.  
  71. public void setAuthor(String author) {
  72. this.author = author;
  73. }
  74.  
  75. public String getPublishing() {
  76. return publishing;
  77. }
  78.  
  79. public void setPublishing(String publishing) {
  80. this.publishing = publishing;
  81. }
  82.  
  83. public Date getPubdate() {
  84. return pubdate;
  85. }
  86.  
  87. public void setPubdate(Date pubdate) {
  88. this.pubdate = pubdate;
  89. }
  90.  
  91. public double getPrice() {
  92. return price;
  93. }
  94.  
  95. public void setPrice(double price) {
  96. this.price = price;
  97. }
  98.  
  99. @Override
  100. public String toString() {
  101. return "书名: " + name + " 图书编号: " + isbn + " 作者: " + author
  102. + " 出版社: " + publishing + " 出版时间: " + pubdate
  103. + " 价格: " + price;
  104. }
  105. }

调用:

  1. package xml;
  2.  
  3. import java.io.InputStream;
  4. import java.util.HashMap;
  5. import java.util.List;
  6. import java.util.Map;
  7.  
  8. import model.Book;
  9.  
  10. import org.junit.BeforeClass;
  11. import org.junit.Test;
  12.  
  13. import util.DateUtils;
  14. import dao.GenericDao;
  15. import dao.JdbcGenericDaoImpl;
  16.  
  17. /**
  18. * 测试泛型DAO的CRUD操作
  19. */
  20. public class GenericDaoTest {
  21.  
  22. private GenericDao<Book> bookDao = new JdbcGenericDaoImpl<Book>();
  23.  
  24. private static InputStream is;
  25.  
  26. @BeforeClass
  27. public static void setUpBeforeClass() throws Exception {
  28. is = XmlParserTest.class.getResourceAsStream("/books.xml");
  29. }
  30.  
  31. @Test
  32. public void testSave() throws Exception {
  33. List<Book> books = SaxHelper.saxReader(is);
  34. for (Book book : books) {
  35. bookDao.save(book);
  36. }
  37. }
  38.  
  39. @Test
  40. public void testStudentFindAll1() throws Exception {
  41. System.out.println("\n-------------更新、删除前,测试查询所有记录--------------------");
  42. List<Book> books = bookDao.findAllByConditions(null, Book.class);
  43. for (Book book : books) {
  44. System.out.println(book);
  45. }
  46. }
  47.  
  48. @Test
  49. public void testDelete() throws Exception {
  50. System.out.println("\n-------------测试删除一条记录--------------------");
  51. bookDao.delete("9787111349662",Book.class);
  52. }
  53.  
  54. @Test
  55. public void testGet() throws Exception {
  56. System.out.println("\n-------------测试查询一条记录--------------------");
  57. Book book = bookDao.get("9787121025389", Book.class);
  58. System.out.println(book);
  59. }
  60.  
  61. @Test
  62. public void testUpdate() throws Exception {
  63. System.out.println("\n-------------测试修改一条记录--------------------");
  64. Book book = new Book();
  65. book.setIsbn("9787121025389");
  66. book.setName("JAVA面向对象编程");
  67. book.setAuthor("孙卫琴");
  68. book.setPublishing("电子工业出版社");
  69. book.setPubdate(DateUtils.string2Date("yyyy-MM-dd", "2006-07-01"));
  70. book.setPrice(50.6);
  71. bookDao.update(book);
  72. }
  73.  
  74. @Test
  75. public void testStudentFindAll2() throws Exception {
  76. System.out.println("\n-------------更新、删除前,测试根据条件查询所有记录--------------------");
  77. Map<String,Object> sqlWhereMap = new HashMap<String, Object>();
  78. //sqlWhereMap.put("t_isbn", "9787111213826");
  79. //sqlWhereMap.put("t_name", "Java");
  80. sqlWhereMap.put("t_publishing", "机械工业出版社");
  81. //sqlWhereMap.put("t_pubdate", new Date(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2007-01-01 12:06:00").getTime()));
  82. List<Book> books = bookDao.findAllByConditions(null, Book.class);
  83. for (Book book : books) {
  84. System.out.println(book);
  85. }
  86. }
  87.  
  88. }

参考:

https://www.cnblogs.com/sunwei2012/archive/2010/10/08/1845938.html

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

JavaSE学习总结(十六)—— 泛型与泛型应用的更多相关文章

  1. [006] - JavaSE面试题(六):泛型

    第一期:Java面试 - 100题,梳理各大网站优秀面试题.大家可以跟着我一起来刷刷Java理论知识 [006] - JavaSE面试题(六):泛型 第1问:什么是泛型? Java泛型( generi ...

  2. python3.4学习笔记(十六) windows下面安装easy_install和pip教程

    python3.4学习笔记(十六) windows下面安装easy_install和pip教程 easy_install和pip都是用来下载安装Python一个公共资源库PyPI的相关资源包的 首先安 ...

  3. 201671010140. 2016-2017-2 《Java程序设计》java学习第十六周

    java学习第十六周-并发        本周,学习了Java中线程,并发的知识,在老师的带领下,进行了对知识的理解学习,以及对实验的运行讲解,对这一块内容掌握的还可以,在自主编程中,也能够完成.线, ...

  4. 学习笔记:CentOS7学习之十六:LVM管理和ssm存储管理器使用

    目录 学习笔记:CentOS7学习之十六:LVM管理和ssm存储管理器使用 16.1 LVM的工作原理 16.1.1 LVM常用术语 16.1.2 LVM优点 16.2 创建LVM的基本步骤 16.2 ...

  5. 风炫安全WEB安全学习第二十六节课 XSS常见绕过防御技巧

    风炫安全WEB安全学习第二十六节课 XSS常见绕过防御技巧 XSS绕过-过滤-编码 核心思想 后台过滤了特殊字符,比如说

  6. 风炫安全Web安全学习第十六节课 高权限sql注入getshell

    风炫安全Web安全学习第十六节课 高权限sql注入getshell sql高权限getshell 前提条件: 需要知道目标网站绝对路径 目录具有写的权限 需要当前数据库用户开启了secure_file ...

  7. TypeScript学习笔记(六):泛型

    认识泛型 TypeScript也实现了类型于C#和Java的泛型以实现类型的参数化,我们先看一个需求: function identity(arg: any): any { return arg; } ...

  8. (C/C++学习笔记) 十六. 预处理

    十六. 预处理 ● 关键字typeof 作用: 为一个已有的数据类型起一个或多个别名(alias), 从而增加了代码的可读性. typedef known_type_name new_type_nam ...

  9. Java开发学习(三十六)----SpringBoot三种配置文件解析

    一. 配置文件格式 我们现在启动服务器默认的端口号是 8080,访问路径可以书写为 http://localhost:8080/books/1 在线上环境我们还是希望将端口号改为 80,这样在访问的时 ...

  10. JavaSE 学习笔记之新特性之泛型(二十)

    泛型:jdk1.5版本以后出现的一个安全机制.表现格式:< > 好处: 1:将运行时期的问题ClassCastException问题转换成了编译失败,体现在编译时期,程序员就可以解决问题. ...

随机推荐

  1. Flask-论坛开发-2-Jinja2模板

    对Flask感兴趣的,可以看下这个视频教程:http://study.163.com/course/courseLearn.htm?courseId=1004091002 1. Jinja2 模板介绍 ...

  2. ThreadPoolExecutor源码解读

    1. 背景与简介 在Java中异步任务的处理,我们通常会使用Executor框架,而ThreadPoolExecutor是JUC为我们提供的线程池实现. 线程池的优点在于规避线程的频繁创建,对线程资源 ...

  3. K Nearest Neighbor 算法

    文章出处:http://coolshell.cn/articles/8052.html K Nearest Neighbor算法又叫KNN算法,这个算法是机器学习里面一个比较经典的算法, 总体来说KN ...

  4. php的四种基本算法

    /* 冒泡算法:结果从小到大,规则类似波浪推动的沙滩,先初始阈值为 0,初始第一次波浪之后,如果发现有左值比右边的大,就改变阈值并且完成波浪推动,重新初始化阈值为0,如此往复,直到没有阈值改变的情况出 ...

  5. laravel5 报错419,form 添加crrf_field 后让然失败,本地环境配置问题

    这个是因为laravel自带CSRF验证的问题 解决方法 方法一:去关掉laravel的csrf验证,但这个人不建议,方法也不写出来了. 方法二:把该接口写到api.php上就好了 方法三: 首先在页 ...

  6. React 表单受控组件

    <!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...

  7. SQL 中GO的作用

    use db_CSharp go select *, 备注=case when Grade>= then '成绩优秀' when Grade< and Grade>= then '成 ...

  8. vue-cli webpack 全局引用jquery

    一.初始化项目 首先,执行vue init webpack F:\ZhaoblTFS\Zeroes\Document\代码示例\vue-cli-webpack-jquery>vue init w ...

  9. apache自带压力测试工具ab的使用及解析

    当你搭建了apache服务器并在上面部署了web网站,在网站运行前,为了使apache服务器的性能得到更好的应用,我们可以先对其进行压力测试.进行压力测试其实非常简单,我们也不用再额外下载安装什么测试 ...

  10. 二本毕业,我是如何逆袭成为BAT年薪40W的Java工程师的?

    身边的师弟师妹经常问到:非计算机专业出身,你是在2年内如何逆袭成BAT年薪40W的资深开发工程师的.其实很简单——努力! 我16年毕业于普通的二本学校,非计算机专业出身,只因为对软件开发感兴趣,所以找 ...