-
Java 语言把内存分为 2 种:栈内存、堆内存。
-
栈内存:方法中定义的基本类型变量和对象的引用变量都在方法的栈内存中。当在一段代码块中定义一个变量时,Java 就在栈内存中为其分配内存空间,当超出变量的作用域后,Java 会自动释放掉为该变量所分配的内存空间。
-
堆内存:存放由 new 运算符创建的对象和数组。在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中创建了一个数组或对象后,同时还会在栈中定义一个引用变量,保存其在堆内存中的地址(对象的句柄)。以后可以在程序中使用栈的引用变量来访问堆中的数组或对象。
引用变量是普通变量,定义时在栈中分配,程序运行到其作用域之外后被释放。
数组或对象本身在堆内存中分配,即使程序运行到使用 new 运算符创建的语句所在的代码块之外,其本身所占的内存也不会被释放,在没有引用变量指向它时,会变为垃圾,不能再被使用,但仍占据内存空间,在随后一个不确定的时间被垃圾回收器收走(释放掉),这也是 Java 比较占内存的原因。 -
-
Java 语言的多维数组不一定是规则的矩阵形式,即每行的元素个数可以不同。用 new 运算符为数组申请内存空间时,二维数组必须指定高层维数。
-
String 类创建的字符串变量,一旦初始化或赋值,它的值和所分配的内存内容就不可再改变。如果改变其值,就会产生一个新的字符串。
-
修饰符。
一个类可以有多个修饰符,且无先后顺序之分,但 abstract 和 final 相互对立,不能同时应用在一个类的定义中。
同一个 Java 程序内,若定义了多个类,则最多只能有一个类声明为 public,此时文件名必须与声明成 public 的类名称相同。类修饰符 含义 public 声明为公共类,可以被任何对象访问 abstract 抽象类,没有实现方法,需要子类提供方法的实现,不能创建该类的实例 final 最终类(非继承类),不能被其他类继承 缺省 只有在相同包中的对象才能使用该类 成员变量修饰符 含义 public 公共访问控制符,可以被任何对象的方法访问 private 私有访问控制符,只允许自己类的方法访问 protected 保护访问控制符,只可以被自己的类及其子类或同一包中的其他类访问,在子类中可以覆盖此变量 缺省 缺省访问控制符,可以被同一个包中的其他类访问 final 最终修饰符,称为最终变量,此变量的值不能被改变 static 静态修饰符,该变量被所有对象共享,即所有实例都可使用该变量 transient 过渡修饰符,系统保留、暂无特别作用的临时性变量 volatile 易失修饰符,可以同时被几个线程控制和修改 一个成员变量若被
static final
同时限定,则其含义即是常量,只能在定义时被赋值。
若只用final
,则必须且只能赋值一次,不能缺省。赋值方式有 2 种:定义变量时;构造方法中。成员方法修饰符 含义 public 公共访问控制符,可以被何对象的方法访问 private 私有访问控制符,只允许自己类的方法访问,不能被继承和覆盖 protected 保护访问控制符,只可以被自己的类及其子类或同一包中的其他类访问,在子类中可以覆盖此变量 缺省 缺省访问控制符,可以被同一个包中的其他类访问 final 最终修饰符,该方法称为最终方法,不能被重载。(对于一些比较重要且不希望被子类重写的方法,使用 final 可增加代码的安全性) static 静态修饰符,无需实例化一个对象就可以调用该方法,不能被重载 abstract 抽象修饰符,只声明方法头,没有方法体,需在子类中实现 synchronized 同步修饰符,在多线程程序中,该修饰符用于在运行前,对它所属的方法加锁,以防止其他线程访问,运行结束后解锁 native 本地修饰符,方法体是用其他语言在程序外部编写的 -
成员变量与局部变量的区别
区别 成员变量 局部变量 定义 类中定义 方法中定义 修饰符 不能被访问控制符及 static 修饰 内存中的存储方式 堆内存(成员变量是对象的一部分,对象存在堆内存) 栈内存 内存中的生存时间 随着对象的创建而存在 随着方法的调用而产生,方法调用的结束而自动消失 未赋初值的默认值 类型的默认值(例外:final 修饰但没被 static 修饰,必须显式赋值) 必须显式赋值 -
创建对象
对象是类的实例,故对象属于某个已知的类。
创建对象的步骤:
(1)声明指向“由类所创建的对象”的变量。Book book;
以类名 Book 作为变量类型在栈内存中定义了一个变量 book,用来指向通过 new 运算符在堆内存中创建的一个 Book 类的实例对象,即 变量 book 是对存放在堆内存中对象的引用变量。
因 book 变量是指向由 Book 类所创建的对象,可将它视为“对象的名称”,简称“对象”,事实上,book 只是对象的名称,是指向对象实体的变量,而非对象本身。
(2)利用 new 运算符创建新的对象,并指派给前面所创建的变量。book = new Book();
-
除了基本类型之外的变量都是引用类型。
成员变量类型 初始值 byte 0 short 0 int 0 long 0L float 0.0F double 0.0D char '\u0000'(表示为空) boolean false 所有引用类型 null -
在类定义内调用方法。若强调是“对象本身的成员”,则使用
this.成员名
,此时 this 即代表调用此成员的对象。 -
参数的传递
基本数据类型 —— 传值方式(数据的值本身)
引用型变量 —— 传址方式(引用变量的值本身。通过方法调用,可以改变对象的内容,但对象的引用变量无法改变) -
匿名对象
new Book().setName("math");
当方法执行完后,这个对象也成了垃圾。
通常在如下 2 种情况使用匿名对象:
(1)若对一个对象只需要进行一次方法调用
(2)将匿名对象作为实参传递给一个方法调用。getSomeone(new MyClass());
-
“封装”是指把变量和方法包装在一个类中,以限定成员的访问,从而达到保护数据的一种技术。
-
实例 —— 类所创建的对象。
-
构造方法
(1)在对象被创建时完成对类对象的初始化工作。
(2)方法名与所在类名完全相同。
(3)构造方法没有返回值,但在定义构造方法时,构造方法名前不能用修饰符 void 来修饰,因为一个类的构造方法的返回值类型就是该类本身。
(4)构造方法定义后,创建对象时就会自动调用它,因此构造方法不需要在程序中直接调用,而是在对象产生时自动执行。
(5)若省略构造方法,Java 编译器会自动为该类生成一个默认的构造方法,程序在创建对象时会自动调用默认的构造方法。默认的构造方法没有参数,方法体也没有代码,即什么都不做。若 class 是public,则默认构造方法也是 public 的。
(6)一旦用户定义了构造方法,系统就不再提供默认的构造方法。
(7)从一个构造方法中调用另一个构造方法,使用this
关键字,且必须写在构造方法的第一行。
(8)构造方法一般是 public 的,因为它们在创建对象时,是在类的外部被系统自动调用的。若是 private,则无法在该类以外被调用。 -
(1)静态成员:被 static 修饰的成员。
静态变量(类变量):属于类的变量,而不属于任何一个类的具体对象,是保存在类的内存空间的公共的存储单元中。静态变量必须独立于方法之外。
静态方法(类方法):属于整个类,故不能操纵和处理属于某个对象的成员,只能处理属于整个类的成员。即 static 方法只能访问 static 成员变量或调用 static 成员方法,在静态方法中不能访问实例变量和实例方法。在静态方法中,不能使用this
和super
,因为 this 代表调用该方法的对象,,但现在静态方法不需要对象来调用,故 this 也自然不应存在于静态方法内部。
理解public static void main(String[] args){}
:
Java 虚拟机需要在类外调用 main() 方法,故该方法的访问权限必须是 public,又因为 Java 虚拟机运行时是系统在开始执行一个程序前,并没有创建 main() 方法所在类的实例对象,故它只能通过类名来调用 main() 方法作为程序的入口,故必须是 static 的。
(2)实例成员:不被 static 修饰的成员。实例成员属个别对象所有,彼此之间不能共享(new 的不同对象各自拥有自己保存自己成员的存储空间,不与其他对象共享)。 -
静态初始化器:由 static 修饰的一对“{ }”括起来的语句组,用来初始化工作。
static { //初始化工作 }
和构造方法的区别:
构造方法 静态初始化器 对每个新创建的对象初始化 对类自身进行初始化 在 new 创建新对象时由系统自动执行 一般不能由程序来调用,是在所属的类被加载入内存时由系统调用执行的 new 创建多少个对象,构造方法就被调用多少次 在类被加载入内存时只执行一次,与创建多少个对象无关(类是在第一次使用时被装载,而不是在程序启动时就装载程序中所有可能要用到的类。) 不是方法,没有方法名、返回值和参数 -
继承
(1)子类的每个对象也是其父类的对象,这是继承性的“即是”性质。若 Sub 继承 Super,则在任何可以使用 Super 实例的地方,都可以使用 Sub 实例,反之则不然,父类对象不一定是它的子类的对象。
(2)使用super()
调用父类中特定的构造方法。
(3)执行子类的构造方法前,若未用super()
调用父类的特定构造方法,则会先调用父类中的无参构造方法,其目的是为了要帮助继承自父类的成员做初始化操作。(故,若父类中只定义了有参构造方法,而在子类构造方法中没用 super() ,则编译时将发生错误 —— 解决:在父类里加上一个不做事的无参构造方法。)
(4)构造方法无法继承。
(5)super()
与this()
均必须放在构造方法的第一行,故super()
与this()
无法同时存在于同一个构造方法内。
(6)和 this 一样,super 也指的是对象,故 super 同样不能在 static 环境中使用,包括静态方法和静态初始化器。
(7)super
可以访问父类的构造方法、成员变量和成员方法。
(8)覆盖
通过父类对象访问子类的成员时,只限于“覆盖”的情况发生时。(声明父类变量 per 指向子类对象 Student,利用父类对象 per 调用子类中覆盖的方法)
向上转型:创建父类类型的变量指向子类对象,即将子类对象赋值给父类类型的变量。是从一个较具体的类到一个较抽象的类之间的转换,所以是安全的。
向下转型:将父类对象通过强制转换为子类型再赋值给子类对象。将较抽象的类转换为较具体的类。(Student stu = (Student)per;
)必须使用显示转换。 -
Object 类
所有类都是直接或间接地继承自 Object 类。若某个类没有使用 extends,则默认为 Object 的子类。
(1)equals() 方法
对于字符串的操作,Java 程序在执行时会维护一个字符串池(String Pool),对于一些可共享的字符串对象,会先在 String 池中查找是否有相同的 String 内容(字符相同),若有就直接返回,而不是直接创建一个新的 String 对象,以减少内存的占用。当在程序中使用“"”括起来的一个字符串时,该字符串就会在 String 池中。“==”:比较 2 个变量本身的值,即 2 个对象在堆内存中的首地址,即是否指向同一个对象。
“equals()”:对于字符串 —— 比较 2 个字符串中所包含的内容是否相同。
对于非字符串类型变量 —— 和“==”一样。
(2)getClass():返回运行时的对象所属的类(Class 类型)。
一个 java.lang.Class 对象代表了 Java 应用程序运行时所加载的类或接口的实例,Class 对象由 JVM 自动产生,每当一个类被加载时,JVM 就自动为其生成一个 Class 对象。Class 类没有构造方法,通过 Object 类的 getClass() 来取得对象对应的 Class 对象。
(3)对象运算符 instanceof:测试指定对象是否是指定类或其子类的实例,是则返回 true。 -
抽象类 abstract class
(1)抽象类不能创建对象,只能由其子类创建对象。专门用作父类。
(2)抽象方法:只需声明,不需实现,用 “;” 结尾,而不是 “{}”。必须被子类覆盖,否则子类仍是 abstract。
(3)抽象类不能用 final 修饰。(因为要继承)
(4)abstract 不能与 private、static、final、native并列修饰同一个方法。
(5)抽象类不一定包含抽象方法,但包含抽象方法的类一定要申明为抽象类。非抽象类中不能存在抽象方法。
(6)抽象类可以有构造方法且可被子类的构造方法调用,但构造方法不能声明为抽象的。一般在抽象类定义构造方法是多余的。 -
接口 Interface
[public] interface 接口名 [extends 父接口名列表] { [public] [static] [final] 数据类型 成员变量名 = 常量; //若省略,则系统默认为 public static final …… [public] [abstract] 返回值数据类型 方法名 (参数表); //若省略,则系统默认为 public abstract }
(1)接口的数据成员都是静态的且必须初始化,即必须是静态常量。
(2)接口的方法必须全部声明为 abstract,即全部是抽象方法。
(3)接口可以作为一种引用类型来使用,可以声明接口类型的变量或数组,并用它来访问实现该接口的类的对象。
(4)接口是多重继承:一个接口可以有一个以上的父接口,用逗号分隔,新接口将继承所有父接口中的变量与方法。 -
内部类
(1)内部类可以声明为 private 或 protected
(2)内部类前面用 final,表明该类不能被继承
(3)可以为 abstract,但要被其他的内部类继承或实现
(4)不能与包含它的外部类同名
(5)内部类既可以使用外部类的成员变量,也可以使用内部类所在方法的局部变量
(6)内部类也可以是一个接口,该接口必须由另一个内部类来实现。
(7)既可以在类中定义,也可以在程序块之内定义(方法中或循环体内部等)。方法中定义的内部类只能访问方法中的 final 局部变量。
(8)内部类若被声明为 static,则静态内部类将自动转化为”顶层类“(top level class),即它没有父类,且不能引用外部类成员或其他内部类中的成员。只有静态内部类才能声明静态成员。 -
匿名内部类
没有类名,在定义类的同时,就生成该类的一个对象,利用它访问类里的成员。( //创建匿名内部类,并执行所定义的方法 new 类名() { //括号 ”()“ 内不允许有参数 方法名(参数 1, 参数 2, …, 参数 n) { 方法体; } } ).方法名(参数 1, 参数 2, …, 参数 n);
-
包的 import:“*” 只能表示本层次的所有类,不包括子层次下的类。
-
Java 语言的垃圾回收
在 Java 程序的生命周期中,Java 运行环境提供了一个系统的垃圾回收器线程,负责自动回收那些没有引用与之相连的对象所占用的内存,这种清楚无用对象进行内存回收的过程叫做垃圾回收(Garbage-Collection)。
当一个对象被创建时,JVM 会为该对象分配一定的内存、调用该对象的构造方法并开始跟踪该对象。当对象停止使用时,JVM 将通过垃圾回收器回收该对象所占用的内存。
系统中任何对象都有一个引用计数器,一个对象被引用 1 次,该对象的引用计数器就加 1,减少一次引用则计数器就减 1。当一个对象的引用计数器减到 0 时,即不被任何引用类型的变量使用时,说明该对象可以回收。
垃圾回收器的好处:
(1)它把程序员从复杂的内存追踪、监测、释放等工作中解放出来。
(2)它防止了系统内存被非法释放,从而使系统更加稳定。
特点:
(1)只有当一个对象不被任何引用类型的变量使用时,它占用的内存才可能被垃圾回收器回收。
(2)不能通过程序强迫垃圾回收器立即执行。
回收时间对程序员透明。可以通过System.gc()
或Runtime.gc()
方法提示垃圾回收器进行内存回收操作,但无法保证立即执行。
(3)当垃圾回收器将要释放无用对象占用的内存时,先调用该对象的finalze()
方法。
当一个对象将要退出生命周期时,可以通过finalze()
方法来释放对象所占用的其他相关资源,但是 JVM 有很大的可能不调用对象的finalze()
方法,因此很难保证使用该方法来释放资源是安全有效的。 -
异常处理
(1)try 块中某条语句执行出现异常时,被启动的异常处理机制就会自动捕获到它,然后流程自动跳过产生异常的语句后面的所有尚未执行的语句,系统直接跳到 catch 块。
(2)无论 try 程序块是否捕获到异常,或者捕获到的异常是否与 catch 后面括号里的异常相同,最后一定会运行 finally 块。finally 块运行结束后,程序再转到 try-catch-finally 块后的语句继续执行。
(3)多异常处理:当 try 块抛出一个异常时,程序的流程首先转向第一个 catch 块,并审查当前异常对象可否被这个 catch 块接收(异常对象与 catch 后面小括号中的参数类型匹配,即 catch 所处理的异常类型与生成的异常对象的类型完全一致或是它的祖先类)。若 try 块产生的异常对象被第一个 catch 块接收,则程序的流程将直接跳转到这个 catch 块中,try 块中尚未执行的语句和其他 catch 块将被忽略。若 try 块产生的异常对象与第一个 catch 块不匹配,系统将自动转到第二个,依此类推直到找到一个可以接收该异常对象的 catch 块,即完成流程的跳转。若所有的 catch 块都不能与当前的异常对象匹配,则说明当前方法不能处理这个异常对象,程序流将返回到调用该方法的上层方法,依此类推,若所有方法中都找不到合适的 catch 块,则由 Java 运行系统来处理这个异常对象。此时通常会终止程序的执行,退出 JVM 返回到操作系统,并在标准输出设备上输出相关的异常信息。
(4)异常对象与 catch 的匹配是按照 catch 的先后排列顺序进行的,故 在处理多异常时应注意认真设计各 catch 的排列顺序。一般,将处理较具体、较常见异常的 catch 放在前面,而可以与多种异常类型相匹配的放在较后位置。若将子类异常的 catch 放在父类异常 catch 的后面,则编译不能通过。
(5)当 try 中语句抛出一个异常时,其后代码不会被执行,可以通过 finally 语句块来为异常处理提供一个统一出口,通常在 finally 中对一些资源做清理工作,如关闭打开文件等。
(6)当 catch 中含有System.exit(0)
语句时,则不执行 finally 块,程序直接终止;当 catch 中含有 return 语句时,则执行完 finally 中的语句后再终止程序。
(7)catch() 括号内只接收由 Throwable 类的子类所产生的对象,其他类均不接收。 -
输入输出
输入输出流按处理数据的类型分为 2 种:
(1)字节流(Byte Steam)(也称为 二进制字节流 Binary Byte Steam,位流 Bits Stream)
每次读写 8 位二进制数,只能将数据以二进制方式读写,不能分解、重组、理解这些数据,故可以使之变换、恢复到原来的有意义的状态。
很多情况下,数据源或目标中含有非字符数据,例如 Java 编译器产生的字节码文件中含有 Java 虚拟机的指令,这些信息不能被解释成字符,所以必须用字节流来输入输出。
字节流也可以处理文本文件,但是不能直接操作 Unicode 字符,若文件中有汉字,则可能会出现乱码。因此 Java 不提倡使用字节流读写文本文件。
(2)字符流(Character Stream)
每次读写 16 位二进制数,并将其作为一个字符,而不是二进制位来处理。
字符流的源或目标通常是文本文件。Java 中的字符使用的是 16 位的 Unicode 编码,每个字符占有 2 个字节。
(3)对象流的关闭最好是放在 finally 块中,此时需要关闭的流对象应在 try 块之前定义;若流对象在 try 中定义,那么关闭流对象的语句可放在 try 块的最后面。 -
多线程
(1)概念- 程序 Program 含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中。是静态的代码。
- 进程 Process 程序的一次执行过程,是系统运行程序的基本单位。是动态的。 系统运行一个程序即是一个进程从创建、运行到消亡的过程。 每个进程间是独立的,除非利用某些通讯管道来进行通信,或是通过操作系统产生交互作用,否则基本上各进程不知道彼此的存在。
- 多任务 Multi task 指在一个系统中可以同时运行多个进程,每个进程都有一段专用的内存区域,即使是多次启动同一段程序产生不同的进程也是如此。 “同时运行”是指由操作系统将系统资源分配给各个进程,每个进程在 CPU 上交替运行,每个进程占有不同的内存空间,内存消耗很大,这使系统在不同的程序之间切换时开销很大,进程之间的通信速度很慢。
- 线程 Thread (轻量级进程 light-weight process) 同类的多个线程共享同一块内存空间和一组系统资源。 进程属于操作系统的范畴,主要是在同一段时间内,可以同时执行一个以上的程序; 线程是在同一程序内同时执行一个以上的程序段。
(2)处于运行状态的线程在下列情况下将让出 CPU 的控制权:
- 线程运行完毕
- 有比当前线程优先级更高的线程处于就绪状态
- 线程主动睡眠一段时间(睡眠时进入阻塞状态,睡眠结束进入就绪状态)
- 线程在等待某一资源
(3)阻塞状态是因为某种原因系统不能执行线程的状态,这种状态下即使 CPU 空闲也不能执行线程。
如下情况可使线程进入阻塞状态:- 调用
sleep()
或yield()
方法 - 为等待一个条件变量,线程调用
wait()
方法 - 该线程与另一线程
join()
在一起
(4)处于消亡状态的线程不具有继续运行的能力。
导致线程消亡的原因:
- 正常运行的线程完成了它的全部工作,即执行完了
run()
方法的最后一条语句并退出。 - 当进程因故停止运行时,该进程中的所有线程将被强行终止。
当线程处于消亡状态,并且没有该线程对象的引用时,垃圾回收器会从内存中删除该线程对象。
(5)被同时激活的多个线程将同时执行,使用
Thread
类的join()
方法可使线程有序执行。当某一线程调用join()
方法时,其他线程会等到该线程结束后才开始执行。(6)若直接使用
Thread()
类,在类中this
即指当前线程;若使用Runnable
接口,要在此类中获得当前线程,必须使用Thread.currentThread()
方法。(7)互斥:两个或多个线程不能同时发生,无先后次序的要求。
同步:两个或多个线程有先后次序的约束。
共享:线程之间对内存数据的共享。(8)同步
Synchronized
当被Synchronized
限定的代码段执行完,就自动释放互斥锁。- 同步语句
Synchronized (对象) 临界代码段 }
- 同步方法
public synchronized 返回类型 方法名() { 方法体 }
public 返回类型 方法名() { synchronized(this) { 方法体 } }
① 在任何时刻,一个对象的互斥锁只能被一个线程所拥有。
② 只有当一个线程执行完它所调用对象的所有 synchronized 代码块或方法时,该线程才会释放这个对象的互斥锁。
③ 临界代码中的共享变量应定义为private
型。否则,其他类的方法可能直接访问和操作该共享变量,这样 synchronized 的保护就失去了意义。
④ 所有对临界代码中共享变量的访问与操作均在 synchronized 代码块中进行。
⑤ 通常共享变量都是私有静态变量。
⑥ 对于一个 static 型的方法,即类方法,要么整个方法是 synchronized,要么整个方法不是 synchronized。
⑦ 若 synchronized 用在类声明中,则表示该类中的所有方法都是 synchronized。(9)线程间的通信
方法 功能 wait()
若一个正在执行同步代码 synchronized
的线程 A 执行了wait()
调用(在对象 x 上),该线程暂停执行而进入对象 x 的等待队列,并释放已获得的对象 x 的互斥锁。线程 A 要一直等到其他线程在对象 x 上调用notify()
或notifyAll()
方法,才能在重获对象 x 的互斥锁后继续执行(从wait()
语句后继续执行)notify()
唤醒正在等待该对象互斥锁的第一个线程。只能在同步代码块里调用 notifyAll()
唤醒正在等待该对象互斥锁的所有线程,具有最高优先级的线程首先被唤醒并执行。只能在同步代码块里调用 (10)当线程的
run()
方法运行结束,则线程进入消亡状态。 -
泛型
(1)定义
泛型所操作的数据类型被指定为一个参数,这个参数称为类型参数(Type Parameters),其实质是将数据的类型参数化。
利用泛型类创建的对象称为泛型对象,这个过程也称为泛型实例化。格式 泛型类 [修饰符] class 类名<T>
泛型接口 [public] interface 接口名<T>
泛型方法 [public] [static] <T> 返回值类型 方法名(T 参数)
(2)设计泛型方法的目的主要是针对具有容器类型参数的方法的。若编写的代码并不接受和处理容器类型,就不需要使用泛型方法。
(3)限制泛型的可用类型
class ClassName <T extends anyClass>
anyClass 指某个类或接口。
T 是 anyClass 或其子类或是实现了 anyClass 接口的类。
若未使用extends
关键字限制泛型的类型参数,则默认是 Object 类下的所有子类。(4)泛型的类型通配符
泛型类名<? extends T> o = null; //上限通配。声明泛型类对象。`? extends T` 表示是 T 或 T 的未知子类或是实现接口 T 的类。 泛型类名<? super T> o = null; //下限通配。表示是 T 或 T 的一个未知父类型
也可以用在方法参数中。
function(Type<? extends T> o) {……}
若只使用?
通配符,则默认是? extends Object
,故?
称为非受限通配。
直接用<?>
创建泛型对象,有 2 个特点:
a. 具有通用性,即该泛型类的其他对象可以赋值给用通配符 "?" 创建的泛型对象,反之不可。
b. 用 "?" 创建泛型对象,只能获取或删除其中的信息,但不可为其添加新的信息。
由于 JVM 只是在编译时对泛型进行安全检查,故要注意:
a. 不能使用泛型的类型参数 T 创建对象。如T obj = new T()
是错误的。
b. 在泛型中可以用类型参数 T 声明一个数组,但不能使用类型参数 T 创建数组对象。如T[] a = new T[个数]
是错误的。
c. 不能在静态环境中使用泛型类的类型参数 T。
d. 异常类不能是泛型的。即泛型类不能继承 java.lang.Throwable 类。(5)被定义为泛型的类或接口可被继承与实现
-
容器类
容器框架的继承关系
(1)Collection 接口
通常不能直接使用,提供了添加、删除元素、管理数据的方法。(2)List 列表接口
包含有序元素的线性表,其中元素可重复,也可为空值 null。
使用迭代器遍历容器;Iterator it = c.iterator(); while (it.hasNext()) { Object o = it.next(); }
(3)Set 集合接口
不含重复元素。-
HashSet 哈希集合
根据哈希码存取元素。
哈希集合是在元素的存储位置和元素的值 k 之间建立一个特定的对应关系 f,使每个元素与一个唯一的存储位置相对应。因而在查找时,只要根据元素的值 k 计算出 f(k) 的值即可,若此元素在集合中,则必定在存储位置 f(k) 上,因此不需要与集合中其他元素比较便可直接取得所查元素。称 f 为哈希函数,按这种关系建立的表称为哈希表或散列表。
HashSet 类不保证迭代顺序,允许元素值为 null。
在比较 2 个加入哈希集合中的元素是否相同时,会先比较哈希码方法 hashCode() 的返回值是否相同,若相同,则再使用 equals() 方法比较其存储位置(内存地址);若两者都相同,则视为相同的元素。
因为不同元素计算出的哈希码可能相同。若重写了元素对应类的 equals() 方法 或 hashCode() 方法,则必须重写另一个,以保证其判断的唯一性。
上座率也称装填因子,值在 0.0~1.0 之间,表示集合的饱和度,当集合中的元素个数超过了容量与上座率的乘积,容量就会自动翻倍。 -
TreeSet 树集合
工作原理与 HashSet 相似,但元素总是处于有序状态。当排序很重要时,应选择 TreeSet。
(4)Map 映射接口
元素成对出现。每个键都是唯一的且最多映射到一个值。-
HashMap
基于哈希表,通过哈希码进行查找,故添加和删除映射关系效率较高,且允许 null 值和 null 键,但必须保证键的唯一性。 -
TreeMap
存在一定顺序,不允许键对象是 null。
-
Java 读书笔记
最新推荐文章于 2021-08-17 08:40:32 发布