1. 麻烦给我详细介绍下java中用了泛型的好处,是不是用了泛型就可以不用NEW去创建对象了
1. 泛型的却很有用, 如果跟反射可以配合用好, 普通企业应用得代码量可以减少非常多.
2. python之类type inference语言比java更适合做这种template抽象
举个例子: 企业程序大部分程序是实体信息管理得, 真正涉及交易清算得只有极小一部分
对于所有得实体管理, 基本上都是增,删, 改, 查看, 查找等等几个功能
我现在用了几个框架, 经典结合方式:webwork+spring+hibernate, 分析如下:
hibernate O/R Mapping没什么花头, 为每个实体管理都要写一个映射类, 一般跟实体得数目差不多, 有时候为了避免关联, 建几个视图映射会多点. But, 这个能用hibernate自己写得工具生成, 维护一个java类文件, 以及里面得meta description.
或者自己写个程序生成( 给定数据源,, 输出java类文件, 以及hbm .xml文件 ), 这个只需要维护sql语句就兴了, 我通常这么做.
1. 不用泛型, 不用反射
spring层要写5个service(假设一个实体对应一个service), 分别实现读实体, 加实体, 删实体, 根据条件查找实体List这些功能.
要写5个不同得实体类得Action, 然后Action里面实现校验, 增加, 修改, 删除, 列表得功能, 每一个类要实现得功能同样也类似.
这些都没多少重构实质性得体现, 好处不明显, 可以提取得公共代码非常少.
2. 不用泛型, 用反射
在 1 得基础之上, 可以重构提取出一个公共service, 将分散在5个service得公共代码kill掉. 多出一个根据实体类参数操作得公共类, 但是类得数量增加一个.
同样Action也可以提出一个baseaction来
重构消去了一部分代码, 每个service得代码还是存在重复代码, 但是这个还可以通过反射合成一个service, 不过掉用者要用反射调用
对于action因为需要配置, 除非webwork支持类型得注入, 否则, 不好消去.
如果webwork spring 支持实体类型得注入, 那么到此可以结束, 只写一套逻辑, 代码也没有重复, 由于精力有限, 没有去深入研究spring AOP跟webwork 得inteceptor机制, 不知道支持不支持.
3.用泛型, 用反射
在1得基础上引入泛型, 写一个泛型service, 写一个泛型webwork action , 也没有研究过xwork中泛型对象是怎么配置产生得, 如果支持配置, 只需要写一个action即可, 如果不支持, 需要为每个实体类写一个webwork action
同样页面也是这个样子
但是, java得泛型用了一把, 不说对很多东西支持得很差, 光写法上来看, 跟C++ template一致, 仍然是一大堆得<>, 让初学者吐血, 变成高手炫耀得奇技淫巧. 比起python之类动态类型安全语言type inference机制相差太远, java泛型还有很长得路要走, 不过静态语言走到type inference上来得话, 编译类型检查得好处就没了, 所以java这方面永远不可能~~~
通过上面考虑, 我觉得python更适合web管理程序, 对于很多entity management得程序完全可以写出一个服务所有, 仅仅是O?Rmapping就没办法省. 不知道python有没有类似java得反射, 如果有得话会更好, 可以写出更通用, 量更少得代码. 相信ruby on rails成功也有这方面得原因(我没用过ruby, 错了也别骂我), 毕竟代码越少越好, 尤其是重复代码
我得主张是kill掉all重复代码, 只要能抽象统一得代码重复就是罪过, 哈哈, 相信用python可以把这个做得更好, 越来越喜欢python语言得特性了, 可惜还没拿他来做过web程序
2. 还是关于java泛型的问题:在静态方法中为什么不能引用类级别的类型参数
你好,我觉得最主要的原因是因为泛型擦除。
class Demo<T> {
public void add(T t) {
//...
}
}
编译后通过反编译:
D:\test>javap -s -p Demo
Compiled from "Demo.java"
public class Demo extends java.lang.Object{
public Demo();
Signature: ()V
public void add(java.lang.Object);
Signature: (Ljava/lang/Object;)V
}
所以,非静态方法正是由于专需要先有对象,属进而在使用泛型的时候可以确定安全检查的限制条件。
Demo<String> d = new Demo<String>() ;
d.add("str") ; //这里你就只能添加String类型的。
而静态方法不需要构件对象,所以他在调用方法是,根本没有判断参数的约束条件,所以虚拟机压根就不希望这种事情发生,所以在编译时就报错了。
3. 大学刚毕业可以学Java吗 Java如何学习
大学毕业是可以学习Java的,Java前景是很不错的,像Java这样的专业还是一线城市比较好,师资力量跟得上、就业的薪资也是可观的,
0基础学习Java是没有问题的,关键是找到靠谱的Java培训机构,你可以深度了解机构的口碑情况,问问周围知道这家机构的人,除了口碑再了解机构的以下几方面:
1. 师资力量雄厚
要想有1+1>2的实际效果,很关键的一点是师资队伍,你接下来无论是找个工作还是工作中出任哪些的人物角色,都越来越爱你本身的技术专业java技术性,也许的技术专业java技术性则绝大多数来自你的技术专业java教师,一个好的java培训机构必须具备雄厚的师资力量。
2. 就业保障完善
实现1+1>2效果的关键在于能够为你提供良好的发展平台,即能够为你提供良好的就业保障,让学员能够学到实在实在的知识,并向java学员提供一对一的就业指导,确保学员找到自己的心理工作。
3. 学费性价比高
一个好的Java培训机构肯定能给你带来1+1>2的效果,如果你在一个由专业的Java教师领导并由Java培训机构自己提供的平台上工作,你将获得比以往更多的投资。
希望你早日学有所成。
4. java泛型问题:我能理解为什么类型参数不能用于静态字段,但不理解为何在静态方法中也不能引用类型参数
这个原理是这样的,静态方法中所能引用的属性必须是静态的,而引用参专数是无法声明为静态的属,因为静态的属性必须在声明时赴值或者实例化,泛型引用变量在声明的时候根本不知道是什么类型,自然不可能实例化,当然也不能放进静态方法了,手机码字,求支持。不懂加qq详说
5. Java中如何在无参构造方法中调用有参构造
一般正常的都是参数多的调用参数少的。有参数的调用无参数的居多。
当然你要无参调用的参的也可以。
你用无参,调用有参的。那你有参的参数你需要怎么传呢。当然如果换一个角度也可以。
我手写下。没经过IDE的。提供下思路。我有两种思路,写同一个功能。
public class Person{
private String name;
private int age;
//无参
public Person(){
this.name="张三";
this.age=20;
}
//有参
public Person(String name){
this.name=name;
}
//多参
public Person(String name,int age){
this(name);//调用一个参数的构造方法
this.age=age;
}
}
这是一种写法比较常用的方法。写第一种。用上面的例子直接写
public Person(){
this("张三",20);//调用有参构造方法。设置默认值。和第一种方法功能一样
}
public Person(String name,int age){
this.name=name;
this.age=age;
}
为了程序易读性。一般。参数的扩展和构造方法的调用都是有一定的规律的。
有参调无参,多参调少参。。扩展参数的时候,最好保持原来的顺序。
6. 请教关于java的泛型方法
Java泛型详解
概述
在引入范型之前,Java类型分为原始类型、复杂类型,其中复杂类型分为数组和类。引入范型后,一个复杂类型
就可以在细分成更多的类型。
例如原先的类型List,现在在细分成List<Object>, List<String>等更多的类型。
注意,现在List<Object>, List<String>是两种不同的类型,
他们之间没有继承关系,即使String继承了Object。下面的代码是非法的
List<String> ls = new ArrayList<String>();
List<Object> lo = ls;
这样设计的原因在于,根据lo的声明,编译器允许你向lo中添加任意对象(例如Integer),但是此对象是
List<String>,破坏了数据类型的完整性。
在引入范型之前,要在类中的方法支持多个数据类型,就需要对方法进行重载,在引入范型后,可以解决此问题
(多态),更进一步可以定义多个参数以及返回值之间的关系。
例如
public void write(Integer i, Integer[] ia);
public void write(Double d, Double[] da);
的范型版本为
public <T> void write(T t, T[] ta);
2. 定义&使用
类型参数的命名风格为:
推荐你用简练的名字作为形式类型参数的名字(如果可能,单个字符)。最好避免小写字母,这使它和其他的普通
的形式参数很容易被区分开来。
使用T代表类型,无论何时都没有比这更具体的类型来区分它。这经常见于泛型方法。如果有多个类型参数,我们
可能使用字母表中T的临近的字母,比如S。
如果一个泛型函数在一个泛型类里面出现,最好避免在方法的类型参数和类的类型参数中使用同样的名字来避免混
淆。对内部类也是同样。
2.1 定义带类型参数的类
在定义带类型参数的类时,在紧跟类命之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取
值范围进行限定,多个类型参数之间用,号分隔。
定义完类型参数后,可以在定义位置之后的类的几乎任意地方(静态块,静态属性,静态方法除外)使用类型参数,
就像使用普通的类型一样。
注意,父类定义的类型参数不能被子类继承。
public class TestClassDefine<T, S extends T> {
....
}
2.2 定义待类型参数方法
在定义带类型参数的方法时,在紧跟可见范围修饰(例如public)之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取值范围进行限定,多个类型参数之间用,号分隔。
定义完类型参数后,可以在定义位置之后的方法的任意地方使用类型参数,就像使用普通的类型一样。
例如:
public <T, S extends T> T testGenericMethodDefine(T t, S s){
...
}
注意:定义带类型参数的方法,骑主要目的是为了表达多个参数以及返回值之间的关系。例如本例子中T和S的继承关系, 返回值的类型和第一个类型参数的值相同。
如果仅仅是想实现多态,请优先使用通配符解决。通配符的内容见下面章节。
public <T> void testGenericMethodDefine2(List<T> s){
...
}
应改为
public void testGenericMethodDefine2(List<?> s){
...
}
3. 类型参数赋值
当对类或方法的类型参数进行赋值时,要求对所有的类型参数进行赋值。否则,将得到一个编译错误。
3.1 对带类型参数的类进行类型参数赋值
对带类型参数的类进行类型参数赋值有两种方式
第一声明类变量或者实例化时。例如
List<String> list;
list = new ArrayList<String>;
第二继承类或者实现接口时。例如
public class MyList<E> extends ArrayList<E> implements List<E> {...}
3.2 对带类型参数方法进行赋值
当调用范型方法时,编译器自动对类型参数进行赋值,当不能成功赋值时报编译错误。例如
public <T> T testGenericMethodDefine3(T t, List<T> list){
...
}
public <T> T testGenericMethodDefine4(List<T> list1, List<T> list2){
...
}
Number n = null;
Integer i = null;
Object o = null;
testGenericMethodDefine(n, i);//此时T为Number, S为Integer
testGenericMethodDefine(o, i);//T为Object, S为Integer
List<Number> list1 = null;
testGenericMethodDefine3(i, list1)//此时T为Number
List<Integer> list2 = null;
testGenericMethodDefine4(list1, list2)//编译报错
3.3 通配符
在上面两小节中,对是类型参数赋予具体的值,除此,还可以对类型参数赋予不确定值。例如
List<?> unknownList;
List<? extends Number> unknownNumberList;
List<? super Integer> unknownBaseLineIntgerList;
注意: 在Java集合框架中,对于参数值是未知类型的容器类,只能读取其中元素,不能像其中添加元素,因为,其类型是未知,所以编译器无法识别添加元素的类型和容器的类型是否兼容,唯一的例外是NULL
List<String> listString;
List<?> unknownList2 = listString;
unknownList = unknownList2;
listString = unknownList;//编译错误
4. 数组范型
可以使用带范型参数值的类声明数组,却不可有创建数组
List<Integer>[] iListArray;
new ArrayList<Integer>[10];//编译时错误
5. 实现原理
5.1. Java范型时编译时技术,在运行时不包含范型信息,仅仅Class的实例中包含了类型参数的定义信息。
泛型是通过java编译器的称为擦除(erasure)的前端处理来实现的。你可以(基本上就是)把它认为是一个从源码到源码的转换,它把泛型版本转换成非泛型版本。
基本上,擦除去掉了所有的泛型类型信息。所有在尖括号之间的类型信息都被扔掉了,因此,比如说一个List<String>类型被转换为List。所有对类型变量的引用被替换成类型变量的上限(通常是Object)。而且,无论何时结果代码类型不正确,会插入一个到合适类型的转换。
<T> T badCast(T t, Object o) {
return (T) o; // unchecked warning
}
类型参数在运行时并不存在。这意味着它们不会添加任何的时间或者空间上的负担,这很好。不幸的是,这也意味着你不能依靠他们进行类型转换。
5.2.一个泛型类被其所有调用共享
下面的代码打印的结果是什么?
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());
或许你会说false,但是你想错了。它打印出true。因为一个泛型类的所有实例在运行时具有相同的运行时类(class),
而不管他们的实际类型参数。
事实上,泛型之所以叫泛型,就是因为它对所有其可能的类型参数,有同样的行为;同样的类可以被当作许多不同的类型。作为一个结果,类的静态变量和方法也在所有的实例间共享。这就是为什么在静态方法或静态初始化代码中或者在静态变量的声明和初始化时使用类型参数(类型参数是属于具体实例的)是不合法的原因。
5.3. 转型和instanceof
泛型类被所有其实例(instances)共享的另一个暗示是检查一个实例是不是一个特定类型的泛型类是没有意义的。
Collection cs = new ArrayList<String>();
if (cs instanceof Collection<String>) { ...} // 非法
类似的,如下的类型转换
Collection<String> cstr = (Collection<String>) cs;
得到一个unchecked warning,因为运行时环境不会为你作这样的检查。
6. Class的范型处理
Java 5之后,Class变成范型化了。
JDK1.5中一个变化是类 java.lang.Class是泛型化的。这是把泛型扩展到容器类之外的一个很有意思的例子。
现在,Class有一个类型参数T, 你很可能会问,T 代表什么?它代表Class对象代表的类型。比如说,
String.class类型代表 Class<String>,Serializable.class代表 Class<Serializable>。
这可以被用来提高你的反射代码的类型安全。
特别的,因为 Class的 newInstance() 方法现在返回一个T, 你可以在使用反射创建对象时得到更精确的类型。
比如说,假定你要写一个工具方法来进行一个数据库查询,给定一个SQL语句,并返回一个数据库中符合查询条件
的对象集合(collection)。
一个方法是显式的传递一个工厂对象,像下面的代码:
interface Factory<T> {
public T[] make();
}
public <T> Collection<T> select(Factory<T> factory, String statement) {
Collection<T> result = new ArrayList<T>();
/* run sql query using jdbc */
for ( int i=0; i<10; i++ ) { /* iterate over jdbc results */
T item = factory.make();
/* use reflection and set all of item’s fields from sql results */
result.add( item );
}
return result;
}
你可以这样调用:
select(new Factory<EmpInfo>(){
public EmpInfo make() {
return new EmpInfo();
}
} , ”selection string”);
也可以声明一个类 EmpInfoFactory 来支持接口 Factory:
class EmpInfoFactory implements Factory<EmpInfo> { ...
public EmpInfo make() { return new EmpInfo();}
}
然后调用:
select(getMyEmpInfoFactory(), "selection string");
这个解决方案的缺点是它需要下面的二者之一:
调用处那冗长的匿名工厂类,或为每个要使用的类型声明一个工厂类并传递其对象给调用的地方,这很不自然。
使用class类型参数值是非常自然的,它可以被反射使用。没有泛型的代码可能是:
Collection emps = sqlUtility.select(EmpInfo.class, ”select * from emps”); ...
public static Collection select(Class c, String sqlStatement) {
Collection result = new ArrayList();
/* run sql query using jdbc */
for ( /* iterate over jdbc results */ ) {
Object item = c.newInstance();
/* use reflection and set all of item’s fields from sql results */
result.add(item);
}
return result;
}
但是这不能给我们返回一个我们要的精确类型的集合。现在Class是泛型的,我们可以写:
Collection<EmpInfo> emps=sqlUtility.select(EmpInfo.class, ”select * from emps”); ...
public static <T> Collection<T> select(Class<T>c, String sqlStatement) {
Collection<T> result = new ArrayList<T>();
/* run sql query using jdbc */
for ( /* iterate over jdbc results */ ) {
T item = c.newInstance();
/* use reflection and set all of item’s fields from sql results */
result.add(item);
}
return result;
}
来通过一种类型安全的方式得到我们要的集合。
这项技术是一个非常有用的技巧,它已成为一个在处理注释(annotations)的新API中被广泛使用的习惯用法。
7. 新老代码兼容
7.1. 为了保证代码的兼容性,下面的代码编译器(javac)允许,类型安全有你自己保证
List l = new ArrayList<String>();
List<String> l = new ArrayList();
7.2. 在将你的类库升级为范型版本时,慎用协变式返回值。
例如,将代码
public class Foo {
public Foo create(){
return new Foo();
}
}
public class Bar extends Foo {
public Foo create(){
return new Bar();
}
}
采用协变式返回值风格,将Bar修改为
public class Bar extends Foo {
public Bar create(){
return new Bar();
}
}
要小心你类库的客户端。