JAVA详细总结(精辟版).pdf
JavaJava 总结总结-开始于 2013-9-7 09:27:48Java 概述Java 分类:java SE:主要是桌面程序、控制台程序开发,是 java 基础java EE:企业级开发(网站开发和基于 b/s 模式的编程)java ME:嵌入式开发(手机软件、小家电)第一个 Java 程序:public class JavaTestprivate String str=new String(hello java.);public void showMessage()System.out.println(str);public static void main(String args)JavaTesttest=new JavaTest();test.showMessage();Java 可以有多个 main 函数。每个类中都可以有 main 函数,也就是说你的代码可以有多个入口,你只需要明白什么时候用哪个类的 main 函数作为入口即可。但是要注意的是用不同入口启动的两个进程是毫不相关的。学习 Java 前的心理准备:java 他妈的把 c/c+剔除的够干净的,在 c/c+中易如反掌的事,在 java 中却要大动干戈。所以在学习 java 之前,请做好心理准备,java 其实是一个残疾人。Java 关键字、运算符Java 保留字一览表:java 中 goto 是保留字,但不是关键字,请记住 java 中没有 goto,但可以实现类似的功能,参考百度。java 中没有 sizeof,因为java 认为 c 语言中的 sizeof 主要用于跨平台,而 java 本身就是跨平台的。java 中没有 unsigned,也就是说 java 中的基本数据类型都是有符号的。这点要注意一下。请注意下面的代码:int a=;int b=;int c=a b;以上代码只有在 a 和 b 都是无符号的 int 时才能正确工作。对于 java 这种没有 unsigned 类型的语言来说,上述代码就是一个编程错误!因为 int 不足以容纳两个有符号 int 的差,当 a是很大的正整数,b 是很大的负整数时,a-b 就会溢出并返回负值。Java 运算符:java 运算符和 c、c+的运算符类似,功能也类似。但也有不同,比如 java 中有无符号右移运算(逻辑右移):。同时注意:用于 String 的“+”和“+=”是 java 中仅有的两个重载过的运算符,java 不允许程序猿重载任何的运算符。Java 数据类型Java 是强数据类型的语言,java 基本数据类型:boolean:true or falsechar:16bits,Unicode 编码字符byte:8bitsshort:16bitsint:32bitslong:64bitsfloat:32bits,float var=32.5f,注意加上“f”,否则带小数点的值都会被 java 当作 double处理。double:64bits在 Java 中,主数据(primitive)类型都有对应的被包装过了的类,以第一个字母大写开头,例如:Boolean、Integer、Character、Byte、Short、Long、Float、Double。主数据类型可以与其对应的对象相互转化,在运算时,Java 5.0 以后会自动进行转换,所以很方便。在使用 ArrayList 时,你只能声明对象,例如:ArrayList(其实这是泛型),而不能使用 ArrayList,但是在使用 ArrayList 时,可以直接使用 int 类型。Java 字节序:java 在任何系统上的字节序都是大端(Big-endian),因为 java 是跨平台的嘛。网络传输的字节序也是大端。因此 java 程序和 java 程序之间进行数据传输,并不会出现什么问题。但是当 java 遇到字节序是小端(Little-endian)的数据时,就会出现问题。这时我们就需要转换数据的大小端。方法有很多,你可以自己写方法实现大小端的检测和转换,但是作为 java程序猿来说,我们应该尽量使用已有的类库或者方法来解决问题,以提高生产率。下面介绍几种 java 类库中已有的解决办法:1使 用 java.nio.ByteBuffer 类 中 的 order 方 法 将 字 节 序 改 为 小 端。如:buffer.order(ByteOrder.LITTLE_ENDIAN);使用 Integer 等基本数据类型对应的类类型中的静态方法将 int 等数据类型的数据转换为小端字节序的数据。如:Integer.reverseBytes(num);另外,在网络传输时,还需要注意其他问题:java 没有 unsigned;java 数据类型的长度和其他语言的可能不同。同时,IEEE 754 标准只是规范了 float、double 的逻辑规范,其存储时还是会有字节序问题,因此在不同系统或者编程语言之间进行传输数据时,也要考虑字节序问题。2byte、int 的转换:由于 java 中所有的基本数据类型都是有符号的,所以当你要把 byte 类型的数据当作无符号数据看待时,你就得使用特殊的方法将其存放在更大容量的数据类型里边,比如将 byte转换之后存放在 int 中。下面说下 byte 和 int 的相互转化:byte 转换为 int 有两种方法:1不改变原值,也即转换前后都将其看作有符号数,可使用强制类型转换:int i=(int)b;2保留最低字节的个个位不变,则需采用位操作:int i=b&0 xff;int 转换为 byte 可以直接使用强制类型转换:byte b=(byte)i;,注意这个操作是直接截取int 中最低一个字节,如果 int 数值大于 127 且小于 256,则转换得到的 byte 为负值(因为是有符号的,但其二进制位和 int 的最低字节完全相同),如果 int 数值大于 255,则转换得到的 byte 就会面目全非,因为截断了。字符串String:String 是不可变字符串对象,任何修改 String 的操作,实际上返回的都是一个全新的String对象。String中保存的是 Unicode 格式的 16位字符组成的字符串,String永远是 Unicode编码。“+”、“+=”可以用来连接字符串。由于 String 是不可变的,因此在连续的字符串连接时,会不断的生成中间的 String 对象,以得到最终的 String 对象。这样的话,效率是很低的,java编译器为了提高效率,会暗地里为我们做一些优化,它用 StringBuilder 来处理中间过程的字符串连接,并最终使用 toString()方法(从 Object 继承来的,当你要打印该对象时,你就要覆盖 toString()方法)将最终结果转换为 String 对象。在程序中,比如在循环连接字符串时,为了效率,最好使用 StringBuilder 来处理中间结果。StringBuilder 是可变的,类似的还有StringBuffer,不过 StringBuffer 是线程安全的,因此开销相对也会大点。byte、char、String 的转化:String.getBytes(“GBK”)取得其“GBK”编码格式的字节数组,“GBK”指出将字符串转换为何种编码并保存到字节数组,取得的字节数组的内容就是该字符串在“GBK”编码下的字节数组。String.getChars()将 String 转为 char。new String(byte bytes,“UTF-8”),将bytes字节数组以“UTF-8”编码格式转换为 Unicode,“UTF-8”指出字节数组是以什么格式编码的,并不是最终要转换成的编码。new String(chars)将 char 类型的数组 chars 转换为 String 类型。数字的格式化Java 中数字的格式化与 C 语言的很相似。将数字以带逗号的形式格式化:String.format(“%,d”,1000000);注意:%和 d 之间有一个逗号。Java 中日期的格式化:String.format(“%tc”,new Date();其中,%tc 表示完整日期,%tr只显示时间,%tA%tB%td 周 月 日也可以这样写:String time=new SimpleDateFormat(yyyy-MM-dd).format(new Date();取得当前日期可以使用 Date 对象,Date 中 getTime()、setTime()都是针对 GMT 时间,toString()则会根据时区将时间转换为相应字符串,但其他关于日期的操作最好使用 Calendar对象,具体参见 Java 手册。Java 控制语句java 中控制语句与 c、c+类似,但与 C/C+不同的是,java 中的 while、if 等的条件判断,不能直接使用类似于 int 型的变量(看其是否等于 0)来判断,应该用一个判断语句或者直接使用 boolean 类型的变量。特殊循环语句:循环 for(int cell:cells)中,冒号(:)代表 in,cells 为数组,每循环一次 cells 数组的下一个元素都会赋值给 cell 变量。这是 java 5.0(Tiger)开始使用的 for 循环加强版,当然你也可以使用 for 原版:for(int i=0;in;i+)foreach 循环可以用于数组和任何实现了 Iterable 接口的类(这并不意味着数组肯定也是一个 Iterable)。类和对象java 中的所有东西都包含在类中,在 java 源文件中可以包括多个类,但是只能有一个public 类,而且如果有 public 类的话,这个文件的名字要和这个类的名字一样。一个类中可以有多个方法,一个方法中可以有多个语句。Java 存取权限和存取修饰符:Java 有 4 种存取权限和 3 种存取修饰符,因为有一个缺省的权限等级(无修饰符)。4 种存取权限:public:.private:.default:只有在同一个包中的默认事物能够存取protected:受保护的部分,运行起来像是 default,但也允许不在同一个包中子类继承受保护的部分3 种存取修饰符:public、private、protected对象与引用:java 中没有所谓的对象变量,有的只是对象的引用,对象只存在于堆上,并且只能通过其引用进行引用。java 创建对象不能通过类似于“Aa;”的形式直接定义,这只是定义了一个对象引用变量,而需要通过类似于“Aa=new A();”的形式在堆上定义一个对象,并将在栈上的对象的引用指向这个对象。c+中对象作为参数,是按照值传递。其实java 也是值传递,只不过java 中只有对象的引用,而没有所谓的“对象变量”,故而传递的都是对象的引用的值,因此在函数中可以改变引用参数指向的对象。注:java 中 String 类型的对象作为函数参数传递时,也是传递引用的值,而不是所谓的“值传递”,因为 String 类是不可变的,它没有提供自身修改的函数,每次修改操作都是新生成一个 String 对象,所以要特殊对待,可以认为是传值。关于这一点,网上实在是有太多的流言蜚语,有很多流言是错误的,请务必注意。那么有没有办法使函数不能够修改引用参数指向的对象呢?答案是传递对象副本的引用。为了使用对象的副本作为函数参数,可以使用对象的 clone()方法对其进行浅层复制(具体细节请百度之),所谓的浅层复制即是只复制对象本身,而不对其引用的或者其成员变量引用的对象进行复制,也即不对整个对象网进行复制。对对象网进行复制(深层复制),另有它法,请百度之。对象相等性的判断:“=”或者“!=”只能用来判断对象的引用是否相等,若需判断对象是否相等,则需equals(),但一般你需要覆盖 equals(),因为 Object 中的 equals()默认使用对象的地址判断对象是否相等。但基本类型直接使用“=”或者“!=”即可。实例变量与局部变量:实例变量是声明在类内,而不是方法中,实例变量有默认值。局部变量是声明在方法中的,局部变量没有默认值,在使用之前,必须初始化,否则编译器报错。关于初始化的更多细节:java 中成员变量可以在定义的后边直接进行初始化,也可以在构造函数中进行初始化。而 c+中成员变量不能在定义类时直接初始化,需通过构造函数初始化。final:声明一个类或方法到达继承树的末尾,可以用 final,也即禁止该类被继承或方法被覆盖。同时请注意 final 方法是前期绑定的,将方法声明为 final 可以有效关闭该方法的动态绑定机制,但多数情况下并不会提升多少效率,因此不要为了提升效率而随意使用 final 来声明方法。静态的方法:用 static 声明的方法是静态的方法,静态方法不需要实例化的对象,就可以通过类名来引用(当然也可以通过对象来引用),但静态的方法不可以使用非静态的变量,也不能调用非静态的方法。静态的变量:用 static 声明的变量是静态变量,静态变量为该类的所有对象共享,注意 static 不能作用于局部变量,只能作用于成员变量,因此只有成员变量才能是静态变量。静态变量也会有默认值,就像实例变量一样会被自动初始化一样。静态变量会在该类任何的静态方法被调用之前被初始化,也会在该类任何的对象被创建之前被初始化。你可以像初始化其他成员变量一样,直接在定义处初始化,也可以使用特殊的语法结构进行初始化静态块,如下:public class Spoon static int i;static i=100;还有一种类似的初始化非静态成员变量的语法实例初始化块,只是没有 static 关键字而已。静态的 final 变量:静态的 final 变量是常数。其声明形式为:public static final var_name;静态的 final 变量必须被显式初始化,可以用以下两种方法之一初始化:1、声明时赋值:public static final double PI=3.1415926;2、在静态初始化程序中:public static final double PI;static PI=3.1415926;Java 终极对象:在 java 中的所有类都是从 Object 这个类继承出来的,也就是说 Object 是所有类的父类,它是非抽象的,允许被实例化。你可以显式声明类的继承对象为 Object:public class Animalextends Object,如果不显式声明的话,java 编译器会隐含的进行继承。ArrayList 就是通过声明 Object 对象为方法参数来处理所有的对象的,否则他不可能实现处理所有对象的功能。super 与 this:通过 super 可以调用父类中的方法,例如:super.isEmpty()。通过 this 可以调用本类中的方法,例如:this.fun()。实际上,java 会将 this 作为一个隐含的参数传递给类中的每一个方法。构造函数:在构造器中调用具有多态行为的函数时,容易引起隐蔽的错误。为此,这里有一条有效的准则:“用尽可能简单的方法使对象进入正常状态,如果可以的话,避免调用其他方法,尤其是别调用具有多态行为的方法”。可以通过 super()调用父类的构造函数,如果子类没有显式的调用父类的构造函数,则编译器会在子类的构造函数第一行加上 super()调用,当然这里只能调用其父类的无参数的构造函数。当然你也可以自己写出具有参数的 super()函数来调用父类的带参构造函数。可以通过 this()从某个构造函数中调用同一个类的另外一个构造函数。this()只能存在于构造函数中,且同 super()一样,只能位于第一行语句。因此 super()与 this()不可兼得。垃圾回收器和对象清理:对于 java 中的垃圾回收器和对象的清理工作,请记住:垃圾回收器可能会被调用,也可能不会被调用,即使其被调用,它也可能以它想要的任何方式来回收垃圾。因此,对它不要抱过多幻想。当下,除了回收内存以外的其他任何事情(比如:已经打开的文件、网络连接、在屏幕上绘制的图形、甚至可以是外部世界的某个开关),你最好不要依赖于垃圾回收器,也不要依赖于 finalize()(finalize()是被垃圾回收器调用的,使程序猿可以在垃圾回收器运行时做出一些重要的动作,比如说回收一些非正常方式分配的内存),最好的办法就是程序猿自己编写好特定的方法调用即可,比如在 finally 中调用之。抽象类、抽象方法抽象类:声明形式为:abstract class Dog extends Animal 抽象方法:抽象方法允许你声明方法的名称、参数以及返回类型,但没有实体,直接以分号结束。例:public abstract void eat();抽象方法的意义是就算无法实现出方法的内容,但还是可以定义出一组子型共享的协议。注意:如果你声明一个抽象方法,那么也必须将该类标记为抽象的,你不能在非抽象类中拥有抽象的方法。第一个具体子类必须实现所有的抽象方法。内部类内部类可以使用外部类所有的方法与变量,就算是私有的,也照样可以使用。当然,这是说内部类的实例可以存取它所属的外部类实例的所有方法与变量。一个内部类被嵌套多少层并不重要,它都能够透明地访问所有它所嵌入的外围类的所有成员。在外部类中创建内部类的实例,就像普通的类的创建一样,例如:MyInner inner=newMyInner();在外部类以外的程序代码创建内部类的实例,要用特殊的语法:MyOuter outer=newMyOuter();MyOuter.MyInner inner=outer.new MyInner();如果你需要生成对外部类对象的引用,可以使用外部类的名字后边紧跟圆点和 this 的特殊语法,如:MyOuter.this使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。也就是说内部类允许以继承多个非接口类型(类或者抽象类)的方式来实现“多重继承”。匿名内部类Object object=new Object().;以上代码创建了一个继承自 Object 的匿名内部类的对象。如果初始化匿名内部类的成员变量,可以直接在变量定义的后边初始化,也可以使用类似于构造器的方法初始化实例初始化块(匿名内部类没有名字,所以也不可能有构造器)。但在初始化时,如果匿名内部类需要用到外部方法中的变量(外部方法的形参或者外部方法的局部变量),那么外部方法中的该变量必须声明为 final 的。同时注意:匿名内部类既可以继承类,也可以扩展接口,但是不管是什么,一次只能一个啊。静态类静态类包含在外部类中,但用 static 修饰,在实例化时不需要有外部类的实例。重载与覆盖java 中,派生类可以随意重载基类中已经拥有的方法,并且使用基类中已经拥有的方法和使用自己重载的方法一样容易,但是如果你想覆盖基类中的方法时最好加上注解:Override,以防止弄错了,没有覆盖,反而成了重载。注意方法的类型由方法名和参数列表和参数类型组成,方法返回值和方法名后边跟的异常说明不属于方法类型的一部分。组合与继承组合:就是在类中定义其他类的引用,并使用之。继承:声明形式为:public class child_name extends father_name 在面向对象的编程中,要谨慎使用继承,因为继承可能会导致脆基类问题,也即当你更改超类时可能会破坏子类。关于应该使用组合还是使用继承,看看是否需要向上转型,如果需要则考虑使用继承,如果不需要则考虑要不要使用继承。多态java 中除了 static 方法(构造器实际上也是 static 方法,只不过该 static 声明是隐式的)和 final 方法(private 方法属于 final 方法)之外,其他所有的方法都是后期绑定。多态正是基于此机制。同时请注意,对于成员变量并不具有多态性啊。接口(interface)类可以有多个接口,接口的意义在于多态!在 C+中允许多重继承,即一个子类可以有多个父类;但在 Java 中是不允许一个子类有多个父类的,这样也是为了简单化,防止致命方块的复杂性。接口可以用来解决多重继承的问题,却也不引起致命方块的问题。接口解决致命方块的方法很简单:那就是把全部的方法设置为抽象的,就相当于 100%的纯抽象类。接口允许创建者确定方法的名字、参数、以及返回类型,但是没有任何方法体,以分号结束。接口也可以包含域,但是这些域隐式的是 static 和 final 的。静态类也可以作为接口的一部分,放到接口中的任何类都自动地是 public 和 static 的。接口的定义:public interface Pet ,定义 Pet 类为接口。接口的实现:public class Dog extends Animal implements Pet ,Dog 继承自 Animal,以 Pet为接口。注意:一个实现该接口的具体类必须实现该接口中的所有方法。枚举(enum)暂时还没东东呢类型信息Java 允许运行时识别对象和类的信息,运行时类型信息识别主要有两种方式:RTTI、反射(折射折射你在哪儿)。RTTI:通过 RTTI(Run-Time Type Information,运行时类型信息),程序能够使用基类的引用来检查这些引用所指的对象的实际派生类型。同时注意:RTTI 实际上允许我们对特定的类进行特定的处理,这对于有过程化编程经验的程序猿来说,是不是很合口味?但是我们在面向对象的编程中,应该尽量使用多态机制,来进行普适操作,然而使用多态机制要求你对基类拥有控制权。因此对于 RTTI 和多态要进行合理取舍。Java 的 RTTI与 Class 对象密切相关。Class:Class 是 java 中的一个类(java.lang.Class),只不过名字有点特殊罢了。那么这个类抽象了什么?它的实例又表示了什么呢?在一个运行的程序中,会有许多类和接口存在。我们就用 Class 这个类来表示对这些类和接口的抽象,而 Class 类的每个实例则代表运行中的一个类。所谓抽象,就是提取这些类的一些共同特征,比如说这些类都有类名,都有对应的hashcode,可以判断类型属于 class、interface、enum 还是 annotation。需要注意的是,这个特殊的 Class 类没有公共构造方法。Class 对象是在加载类时由Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个 Class 对象。虚拟机为每种类型管理一个独一无二的 Class 对象。也就是说,每个类(型)都有一个Class 对象。运行程序时,Java 虚拟机(JVM)首先检查是否所要加载的类对应的 Class 对象是否已经加载。如果没有加载,JVM 就会根据类名查找.class 文件,并将其 Class 对象载入。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字void 也都对应一个 Class 对象。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。一般某个类的 Class 对象被创建,它就用来创建这个类的所有对象。获取 Class 对象的三种方法:1调用 Object 类的 getClass()方法来得到 Class 对象,这也是最常见的产生 Class 对象的方法。2使用 Class 类的中静态 forName()方法获得与字符串对应的 Class 对象。3获取 Class 类型对象的第三个方法非常简单。如果 T 是一个 Java 类型,那么T.class就代表了匹配的类对象。类型转换前的检查:java 会在类型转换之前进行严格的检查,确保类型转换的正确性。主要有以下几种形式:1传统的强制类型转换,如 B b=(B)a;由 RTTI 确保类型转换的正确性。2代表对象的类型的 Class 对象。通过查询 Class 对象可以获取运行时所需的信息。3使用关键字 instanceof,它返回一个布尔值,告诉我们对象是不是某个特定类型的实例。如:if(x instanceof Dog)(Dog)x).bark();对 instanceof 有比较严格的限制:只可将其与命名类型进行比较,而不能与 Class 对象所比较。Class 类提供了 isInstance()方法,可以用来动态的测试对象的类型。反射:RTTI 有一个限制:这个类型在编译时必须是已知的,这样才能使用 RTTI 识别它。但当你获取了一个指向某个并不在你的程序空间中的对象的引用,你该如何使用这样的类呢?这时反射闪亮登场。java.lang.Class 类和 java.lang.reflect 类库一起对反射的概念进行了支持。该类库包含了Field、Method 以及 Constructor(每个类都实现了 Member 接口)。这些类型的对象是由 jvm在运行时创建的,用以表示未知类里对应的成员。这样你就可以使用 Constructor 创建新的对象,用get()和 set()方法读取和修改 Field 对象关联的字段,用 invoke()方法调用与 Method对象关联的方法。另外,还可以调用getFields()、getMethods()、getConstructors()等很便利的方法,以返回表示字段、方法以及构造器的对象的数组。这样,匿名对象的类信息就能在运行时被完全确定下来,而在编译时不需要知道任何的事情。重要的是,要认识到反射机制并没有什么神奇之处。当通过反射与一个未知类型的对象打交道时,jvm 只是简单地检查这个对象,看它属于哪个特定的类(就像RTTI 那样)。在用它做其他事情之前必须先加载那个类的 Class 对象。因此,那个类的.class 文件对于 jvm 来说必须是可获取的:要么在本地机器上,要么可以通过网络取得。所以 RTTI 和反射之间真正的区别只在于,对 RTTI 来说,编译器在编译时打开和检查.class 文件。而对于反射机制来说,.class 文件在编译时是不可获取的,所以是在运行时打开和检查.class 文件。通常你不需要直接使用反射工具,但是它们在你需要创建更加动态的代码时会很有用。反射在 java 中是用来支持其他特性的,例如对象序列化和 JavaBean。Java 中的泛型泛型意味着更好的类型安全性。不过,基本类型无法作为类型参数,这是 java 泛型的一个限制,不过在 java 5.0 以后基本类型会和它的包装类型进行自动转换,这个是不是很方便了啊。定义泛型的两种方式:1、使用定义在类声明或接口声明的类型参数,如下:public classArrayList extends AbstractList implementsAbstractInterface public boolean add(E o)public interface AbstractInterface E fun(E e);2、使用未定义在类声明或接口声明的类型参数,也即泛型方法。对于一个 static 的方法而言,它无法访问泛型类的类型参数,它只能使用泛型方法。泛型方法定义如下:public void takeThing(ArrayList list)或者public void takeThing(ArrayList list)java 泛型由擦除实现:然而遗憾的是,java 中的泛型是个伪泛型。由于历史的原因,为了迁移的兼容性,java泛型由擦除(类型擦除)实现。java 泛型只在程序源码中存在,编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除,在编译后的字节码文件中,就已经被替换为原来的原生类型(Raw Type,也称为裸类型)了,并且在相应的地方插入了强制转型代码,因此对于运行期的 Java 语言来说,ArrayList与 ArrayList就是同一个类。所以说泛型技术实际上是 Java 语言的一颗语法糖。一般而言,你会发现泛型在类或方法的边界处很有效,而在类或方法的内部,由于泛型类型参数被擦除,泛型变得不大有效了。这意味着:在泛型代码的内部,你无法获得任何有关泛型参数类型的信息,你无法知道确切类型信息,任何在运行时需要知道确切类型信息的操作都将无法进行。如果你确实需要一个确切的类型信息,你可以传入你的类型的 Class 对象,然后就可以使用 Class 对象的 isInstance()、newInstance()等方法。协变、逆变、上界、下界:拿 ArrayList 为例,如果 S 是 T 的子类型,那么 ArrayList是 ArrayList的子类型,就称 ArrayList 是协变的;相反,如果 ArrayList是 ArrayList的子类型,那么 ArrayList是逆变的;既不是协变又不是逆变的是不变的,不变的又叫严谨的。java 数组是协变的,如果 Number 是 Integer 的超类型(事实也是如此),那么 Number也是 Integer的超类型,但是协变数组在写入数组的操作时可能会出安全问题,为此,java把每个数组对像在创建时附标一个类型,每当向数组存入一个值,编译器插入一段代码来检查 该 值 的 运 行 时 类 型 是 否 等 于 数 组 的 运 行 时 类 型。如 果 不 匹 配,会 抛 出 一 个ArrayStoreException。虽然 java 的数组是协变的,但 java 的泛型是“不变”的,不过在使用类型时可以通过使用 extends 指定类型边界的上界和 super 指定类型边界的下界来达到协变逆变的目的。以下是一些关于通配符、extends 以及 super 的规则:与 等同,而 则是指定了一个具体的类型;与 等同,?为通配符;指定类型的上界,表示参数化类型可能是 T 或是 T 的子类;指定类型下界,表示参数化类型可能是 T 或是 T 的超类,直至 Object。使用 extends 和 super 时很容易产生迷惑,以下代码注释掉的部分编译错误:static class Foodstatic class Fruit extends Foodstatic classApple extends Fruitstatic class RedApple extends AppleList flist=new ArrayList();/complie error:/flist.add(newApple();/flist.add(new Fruit();/flist.add(new Object();flist.add(null);/only work for null其中,List 表示“具有任何从 Fruit 继承类型的列表”,实际上是将 List 声明为协变的,但由于编译器无法确定 List 所持有的是 Fruit 的哪个具体子类型,所以无法安全的向其中添加对象,但是你可以从中获取元素。相反,你可以向 List类型的List 中添加元素,却无法获取元素。实际上,在使用extends(协变)和 super(逆变)时,应该遵循 PECS 法则。PECS 法则:PECS 指“Producer Extends,Consumer Super”。生产者需要用 extends(协变),比如说方法的返回值(作为生产目的),比如说你要从一个集合中取值。消费者需要用 super(逆变),比如说方法的参数(作为消费目的),比如说向集合赋值。如果你是想遍历 collection,并对每一项元素操作时,此时这个集合时生产者(生产元素),应该使用 Collection;如果你是想添加元素到 collection 中去,那么此时集合时消费者(消费元素)应该使用 Collection。如果你想要一个集合内的类型是一个范围,并且同时能够取值和赋值,这是做不到的,这时候你只能用固定类型,比如 List。我们还可以这样理解:子类生产父类用。比如:函数的参数是消费者,函数参数指定了更加超类的东东,调用该函数就需要生产东东,所以调用的地方就需要更加具体化的子类。但是返回值正好相反,函数返回更加具体化的东东,你需要更加超类的东东来保存之。继承中的协变逆变:c+、java、scala 都支持返回值协变,也就是说在继承层次中子类覆盖超类的方法时,可以指定返回值为更具体的类型。c#不支持返回值协变。允许参数逆变的面向对象语言并不多c+、java、scala 和 c#都会把它当成一个函数重载。使用类型参数实例的方法或变量:在泛型代码内部,擦除会进行到类型边界,如果没有指定类型边界的话,类型边界就默认是 Object。此时,当你在泛型代码的内部,使用一个类型参数实例的方法或者字段时,由于类型信息被擦除了到了 Object,故编译器就会报告错误:说该方法或字段不存在。要想使用类型参数实例的方法或者变量该怎么办呢?就得使用 extends 或 super 指定类型边界。这样一来,你就会觉得 java 泛型的自由性太差,实际上就是如此。使用泛型:如上所述,定义泛型类库非常之复杂,但使用泛型类库相对简单一些。在使用泛型类创建对象时,必须指定类型参数的值。在使用泛型方法时,却可以像调用普通方法一样,而无需指定类型,因为编译器会进行类型参数推断。何时该使用泛型:何时应该使用泛型?历史导致了 java 的泛型算不上真正的泛型,java 泛型唯一的作用就是编译期类型检查,还有就是适应 C+程序员的编程习惯。至于说泛型可以消除强制转换,也只是在掩耳盗铃而已。泛型的其他作用实际上可以用继承、多态等方式替代。java 程序员乱用泛型都是装 B 而已。数组在 java 中,数组是一种效率最高的存储和随机访问对象引用序列的方式。java 会对数组进行边界检查,一旦出现数组访问越界,就会出现运行时错误(异常)。数组也是对象:数组中的元素都是变量,要么是 8 种基本数据类型中的一种,例:int array_name=newint7;要么是引用变量,例:Dog dogs=new Dog7;数组中的变量都是 Dog 引用,故还需创建 Dog 对象,使数组指向新创建的 Dog 对象。基本类型的数组如果不进行显式的初始化,会自动初始化为 0,对象数组会被初始化为 null。多维数组:在大多数语言里,创建多维数组就像一个方阵,但在 Java 里,二维数组就是数组的数组,三维数组就是数组的数组的数组。比如:4*2 的数组,在 java 里,是由5 个数组连接而成的。比如:int a2d=new int42;上面的代码会让 java 虚拟机创建出 4 个元素的数组,这些元素实际上是对 2 个元素数组的引用变量。对于多维数组的操作,大部分与 C 语言类似。另外,多维数组中构成矩阵的每个向量都可以具有任意的长度,这被称之为粗糙数组。Arrays:java.util.Arrays 是工具类,该类包含了操作数组的各种静态方法(比如排序和搜索),该类也包含一个静态工厂,允许将数组视为列表。Arrays.equals():比较两个数组是否相等。此比较是基于内容的。Arrays.deepEquals():用于多维数组是否相等。Arrays.fill():用同一个值填充数组,或者是同一个基本类型,或者是对象的同一个引用。Arrays.sort():对数组进行排序。Arrays.binarySearch():对已经排序的数组进行二分查找。Arrays.asList():接收任意的序列或数组作为其参数,将其转换为 List 容器。Arrays.copyOf():复制得到新的数组,内部实现是调用 System.arraycopy()。System.arraycopy():复制数组,速度比 for 循环要快很多,它针对所有类型做了重载。基本类型数组