您的位置:

首页 >

888真人官网 >

深入理解JAVA中的聚集和组合的区别与联系 >

深入理解JAVA中的聚集和组合的区别与联系

2016-02-06 09:23:13

分类:888真人官网

选自《JAVA语言程序设计-基础篇(原书第8版)》定义:一个对象可以包含另一个对象。这两个对象之间的关系称为组合(composition)。组合实际上是聚集关系的一种特殊形式。聚集模拟了具有(has-a)关系,表示两个对象之间的归属关系。归属关系中的所有者对象称为聚集对象(aggregation object),而它的类称为聚集类(aggregating class)。归属关系中的从属对象称为被聚集类(aggregated object),而它的类被称为被聚集类(aggregated class)。一个对象可以被几个其他聚集对象所拥有。如果一个对象只归属于一个聚集对象,那么它和聚集对象之间的关系就称为组合(composition)。例如:“一个学生有一个名字”就是组合关系,“一个学生有一个地址”就是聚集关系,因为一个地址可以被几个学生所共享。UML中,实心菱形表示组合关系,空心菱形表示聚集关系。如图:聚集关系通常被表示为聚集类中的一个数据域,如图:聚集还可以存在于同一类的多个对象之间。例如:一个人可能有一个管理者或者多个管理者。一个管理者可用变量表示,多个则可使用数组,如图:到这里,基本上聚集和组合的关系就不言自明了。以上这篇深入理解JAVA中的聚集和组合的区别与联系就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

1. 泛型究竟是什么?  在讨论类型推导(type inference)之前,必须回顾一下什么是泛型(Generic).泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。通俗点将就是“类型的变量”。这种类型变量可以用在类、接口和方法的创建中。理解Java泛型最简单的方法是把它看成一种便捷语法,能节省你某些Java类型转换(casting)上的操作:List<Apple> box = new ArrayList<Apple>();box.add(new Apple());Apple apple =box.get(0);上面的代码自身已表达的很清楚:box是一个装有Apple对象的List。get方法返回一个Apple对象实例,这个过程不需要进行类型转换。没有泛型,上面的代码需要写成这样:Apple apple = (Apple)box.get(0);当然,泛型绝不像我在这里描述的这么简单,但这不是我们今天的主角,对于泛型还不是很明白的同学需要补课了~当然,最好的参考资料还是官方文档。2. 泛型带来的问题(Java 7之前)泛型的最大优点是提供了程序的类型安全同时可以向后兼容,但也有让开发者不爽的地方,就是每次定义时都要写明泛型的类型,这样显示指定不仅感觉有些冗长,最主要是很多程序员不熟悉泛型,因此很多时候不能够给出正确的类型参数,现在通过编译器自动推断泛型的参数类型,能够减少这样的情况,并提高代码可读性。3. Java 7中对于泛型的类型推导方面的改进在Java 7以前的版本中使用泛型类型,需要在声明并赋值的时候,两侧都加上泛型类型。比方说这样:Map<String,Integer> map = new HashMap<String,Integer>(); 很多人当初肯定和我一样,对此感到很不解:我在变量声明中不是已经声明了参数类型了吗?为什么在对象初始化的时候还要显示的写出来?这也是泛型在一开始出现的时候受到很多人吐槽的地方。不过,让人欣慰的是,java在进步的同时,那些设计者们也在不断的改进java的编译器,让它变的更加智能与人性化。这里,就是我们今天的主角:类型推倒...额...不是推倒,是类型推导,即type inference,这哥们儿的出现,再写上面这样的代码的时候,可以很开心地省略掉对象实例化时的参数类型,也就变成了这个样子:Map<String,Integer> map = new HashMap<>();在这条语句中,编译器会根据变量声明时的泛型类型自动推断出实例化HashMap时的泛型类型。再次提醒一定要注意new HashMap后面的“<>”,只有加上这个“<>”才表示是自动类型推断,否则就是非泛型类型的HashMap,并且在使用编译器编译源代码时会给出一个警告提示(unchecked conversion warning)。这一对尖括号"<>"官方文档中叫做"diamond"。但是,这时候的类型推导做的并不完全(甚至算是一个半成品),因为在Java SE 7中创建泛型实例时的类型推断是有限制的:只有构造器的参数化类型在上下文中被显著的声明了,才可以使用类型推断,否则不行。例如:下面的例子在java 7无法正确编译(但现在在java8里面可以编译,因为根据方法参数来自动推断泛型的类型):List<String> list = new ArrayList<>();list.add("A");// 由于addAll期望获得Collection<? extends String>类型的参数,因此下面的语句无法通过list.addAll(new ArrayList<>());4. 在Java8中的再进化在最新的java官方文档之中,我们可以看到对于类型推导的定义:Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable. The inference algorithm determines the types of the arguments and, if available, the type that the result is being assigned, or returned. Finally, the inference algorithm tries to find the most specific type that works with all of the arguments.简言之,类型推导也就是指编译器能够根据你调用的方法和相应的声明来确定需要的参数类型的能力。并且官方文档中还给出了一个例子加以诠释:static <T> T pick(T a1, T a2) { return a2; }Serializable s = pick("d", new ArrayList<String>());在这里,编译器能够推导出传入pick方法中的第二个参数的类型是Serializable的。在之前的java版本当中,上面的例子要能够通过编译的话需要这要写:Serializable s = this.<Serializable>pick("d", new ArrayList<String>());这样写的详细原因可以在Bruce Eckel的java编程思想(第四版)的泛型一章看得到,当然这本书是基于java6的,这个版本还没有类型推导这个概念。看到这里,很多人已经明显能看得出来最新版本中类型推导的强力之处了。已经不仅仅局限于泛型类的声明与实例化过程了,而是延伸到了具有泛型参数的方法当中了。4.1 类型推导和泛型方法(Type Inference and Generic Methods)关于新版本中的类型推导和泛型方法,文档中还给了一个稍微复杂一点的例子,我在这里贴出来,原理和上面的Serializable例子都是一样就不再赘述,想巩固的可以再看一下:public class BoxDemo { public static <U> void addBox(U u, java.util.List<Box<U>> boxes) { Box<U> box = new Box<>(); box.set(u); boxes.add(box); } public static <U> void outputBoxes(java.util.List<Box<U>> boxes) { int counter = 0; for (Box<U> box: boxes) { U boxContents = box.get(); System.out.println("Box #" + counter + " contains [" + boxContents.toString() + "]"); counter++; } } public static void main(String[] args) { java.util.ArrayList<Box<Integer>> listOfIntegerBoxes = new java.util.ArrayList<>(); BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes); BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes); BoxDemo.addBox(Integer.valueOf(30), listOfIntegerBoxes); BoxDemo.outputBoxes(listOfIntegerBoxes); }}上面这段代码输出为:Box #0 contains [10]Box #1 contains [20]Box #2 contains [30]提一下,泛型方法addBox重点就在于在新java版本中你不需要再在方法调用中进行显示的类型说明,像这样:BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);编译器能够从传入addBox中的参数自动推断出参数类型是Integer.4.2 类型推导与泛型类和非泛型类的泛型构造器(Type Inference and Generic Constructors of Generic and Non-Generic Classes)额...这个也许英语的更好断句一点:Type Inference and Generic Constructors of Generic and Non-Generic Classes其实,泛型构造器并不是泛型类的专利品,非泛型类也完全可以有自己的泛型构造器,看一下这个例子:class MyClass<X> { <T> MyClass(T t) { // ... }}假如对 MyClass类做出下面这样的实例化:new MyClass<Integer>("")OK,这里我们显示地指出了MyClass的泛参类型X是Integer,而对于构造器,编译器根据传入的String对象("")推导出形式参数T是String,这个在java7版本之中已经实现了,在Java8中有了什么改进呢?在Java8之后,对于这种具有泛型构造器的泛型类的实例化我们可以这么写:MyClass<Integer> myObject = new MyClass<>("");对,还是这一对尖括号(<>),江湖人称diamond,这样我们的编译器就能够自动推导出形式参数X是Integer,T是String了。这个其实和我们一开始Map<String,String>的例子很像,只是多了个构造器的泛型化。需要注意的是:类型推导只能根据调用的参数类型、目标类型(这个马上会讲到)和返回类型(如果有返回的话)进行推导,而不能根据程序后面的一些需求来进行推导。4.3 目标类型(Target Type)前文已经提到过,编译器能够根据目标类型进行类型推导。一个表达式的目标类型指的是一种编译器根据表达式出现的位置而需要的正确的数据类型。比如这个例子:static <T> List<T> emptyList();List<String> listOne = Collections.emptyList();在这里,List<String>就是目标类型,因为这里需要的是List<String> ,而Collections.emptyList()返回的是List<T> ,所以这里编译器就推断T一定是String。这个在Java 7 和 8 中都OK。但是在java 7 中,在下面这种情况中就不能正常编译了:void processStringList(List<String> stringList) { // process stringList}processStringList(Collections.emptyList());这个时候,java7就会给出这种错误提示://List<Object> cannot be converted to List<String>原因:Collections.emptyList()  返回的是List<T> ,这里的T需要一个具体类型,但是因为不能从方法声明中推断出所需的是String,所以编译器就给T了一个Object的值,很明显,List<Object>不能转型到List<String>.所以在java7版本中你需要这样调用这个方法:processStringList(Collections.<String>emptyList());但是,在java8中,由于目标类型概念的引入,这里,很明显编译器需要的是List<String> (也就是这里的Target Type),所以编译器推断返回的List<T>中的T一定是String,所以processStringList(Collections.emptyList());这种描述是OK的。目标类型的使用在Lambda表达式中优势最为明显。总结好了,以上就是关于java中类型推导的一些个人见解,总结来说,越来越完善的类型推导就是完成了一些本来就感觉很理所当然的类型转换工作,只是这些工作满满地全交给了编译器去自动推导而不是让开发者显示地去指定。希望这篇文章的内容对大家学习Java能有所帮助,如果有疑问可以留言交流。

一、安装JDK1、 JVM(Java Virtual Machine—Java虚拟机)JRE(Java Runtime Environment—Java运行时环境)JDK(Java Development kit—Java开发工具包)2、JDK包含了JRE和JVM,所以安装了JDK就安装了JRE和JVM,JDK的安装包可以到Oracle官网上下载或者第三方网站下载,以下提供官方下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html(版本很多,自行选择适用版本,省略安装过程,安装时候下一步下一步即可)二、配置环境变量JAVA_HOME 配置JDK安装路径PATH 配置JDK命令文件的位置CLASSPATH 配置类库文件的位置1、我的电脑(右键)-->属性2、高级系统设置-->环境变量3、系统变量自带是没有JAVA_HOME这个变量,需要自己编辑如下:(变量值就是JDK安装的路径)4、PATH在系统变量中本来就是存在的,选中PATH-->编辑-->在最前面输入Bin的路径(用分号隔开其他路径)5、系统自带的变量是没有CLASSPATH的,需要添加-->用”.”代表当前路径-->用”;”隔开-->输入Bil的路径(如我的路径是:C:\Program Files\Java\jdk1.8.0_60\lib)配置到这里就结束了,记得要按确定,没确定是不会自己保存的。三、验证配置环境变量打开运行àcmdà输入java/javac进行验证(验证成功的效果如下)如果你看到这样的效果,恭喜你配置成功了。以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相信有些同学跟我一样,曾经对这个问题很疑惑。在网上也看了一些别人说的观点,评论不一。有说有值传递和引用传递两种,也有说只有值传递的,这里只说下个人见解先给大家介绍下概念值传递:(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值。引用传递:(形式参数类型是引用数据类型参数):也称为传地址。方法调用时,实际参数是对象(或数组),这时实际参数与形式参数指向同一个地址,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,这个结果在方法结束后被保留了下来,所以方法执行中形式参数的改变将会影响实际参数。先看一个例子public class Test1 { public static void main(String[] args) { int a = 10; changeNum(a); System.out.println("main a=" + a); } static void changeNum(int a) { a = 100; System.out.println("changeNum a=" + a); }}结果:changeNum a=100main a=10说明对于基本的数值类型其实传递的是一份数值的拷贝,传入函数之后跟原来的值就没有关系了,在函数中改变这个数的值也不会影响到原来的值。再看一个例子public class P { String name = "P"; public P(String name) { this.name = name; } @Override public String toString() { return name; }} public class Test { static P p1 = new P("p1"); public static void main(String[] args) { P p = new P("P");     System.out.println("before change p:" + p.toString); changeObj(p); System.out.println("after change p:" + p.toString()); } static void changeObj(P p) { p = new P("pp"); System.out.println("change p:" + p.toString()); //p = p1; //System.out.println(p.toString()); }}这个例子中的运行结果会是什么呢?在这个例子中传入的是一个对象类型的数据。我们先来猜想一下:如果传入的是一个对象的引用,在函数中改变了这个引用所指向的对象,那么外部的引用"p"应该是指向了新创建的P("pp")的对象才对所以猜想的结果是:before change p:Pchange p:ppafter change p:pp那让我们来实际运行一下看看结果,实际的结果是:before change p:Pchange p:ppafter change p:P想猜想的不一样!!!这就让我们很疑惑了,为什么函数没有改变引用所指向对象呢?其实是这样的,首先要理解“=”赋值操作的意义,对于基本类型来说“=”赋值操作是直接改变内存地址上的值,而对引用类型来说“=”赋值其实是改变引用变量所指向的内存地址。赋值操作后引用的赋值并没有对原来的对象造成影响,"hello"对象仍然是存在的,只是str又指向了新的"world"对象理解了赋值操作后,现在来说下函数的参数传递 函数的参数实际上就是函数内部的一个局部变量,从外部传值给这个参数实际上就是一个赋值的过程。来看看第一个例子,传一个定义一个int类型名称为a的形式参数,其实完全可以把名称改为b然后把外部变量a的值传递给函数的变量,就是赋值。基本类型的赋值就是把外部a变量的10赋值给函数变量a,所以这其实是两个变量,两者之间没有任何关系,所以对函数内部变量的改变也不会对外部的变量造成影响。再来说下引用类型的传递,其实是一个道理。在传递的时候是把一个对象的内存地址赋值给函数内部的引用类型的局部变量如上图所示,两个变量是指向同一个对象的。在第二个例子中,我们在函数内部新创建了一个对象,并重新赋值给内部变量p,其实是改变了内部变量的引用所指向的对象,如下图所示所以对外部的p变量是没有影响的。总结:函数参数传递其实是一个赋值的过程,基本类型传递的是数值,引用类型传递的引用对象的内存地址。另外一点要特别注意,函数的参数其实是函数内部的局部变量。不要跟外部变量混淆.以上所述是小编给大家介绍的Java中函数是值传递还是引用传递问题,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

题目:利用条件运算符的嵌套来完成此题:学习成绩> =90分的同学用A表示,60-89分之间的用B表示,60分以下的用C表示。程序分析:程序分析:(a> b)?a:b这是条件运算符的基本例子。程序设计:import javax.swing.*;public class ex5 { public static void main(String[] args){ String str=""; str=JOptionPane.showInputDialog("请输入N的值(输入exit退出):"); int N; N=0; try{ N=Integer.parseInt(str); } catch(NumberFormatException e){ e.printStackTrace(); } str=(N>90?"A":(N>60?"B":

焦点访谈

最新最热的文章

更多 >

COPYRIGHT (©) 2017 Copyright ©2017 888真人 网站地图

联系我们

827570882

扫描二维码分享到微信