——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
目录:
1. Java的诞生
2. Java关键特性
3. Java重要版本
4. Java有关术语
5. JDK目录树
1. Java 的诞生
- 1996年,Sun发布Java的第一个版本。
- 2004年,Java5.0发布。
(泛型类型、“for each”循环、自动装箱、元数据、枚举、静态导入) - 2009年,Sun被Oracle收购。
- 2011年,Java 7。
(基于字符串的switch、异常处理改进)
2. Java关键特性
简单性:与C++比较Java语法是C++语法的一个“纯净”版本。没有头文件、指针运算、结构、联合操作符重载、虚基类等。
简单的另一方面是小。Java支持能够在小型机器独立运行的软件。跨平台性:Java在平台之上提供了一个运行环境JRE,包含JVM(Java虚拟机)、类库和核心文件,不同的平台JRE不同。Java程序源文件编译后生成的同一个字节码文件可以运行于不同的JRE之上,所以Java程序具有平台无关性 。< java程序源代码通过JAVA编译器生成与平台无关的字节码文件,通过JVM解释执行>
面向对象
面向对象是一种设计思想。Java是一门面向对象的编程语言,所以也具有面向对象语言的特点:封装:把数据和对数据的操作封闭起来。抽象即从具体的实例中抽取共同的性质形成的一个概念。
继承:通过继承,子类拥有父类的属性和功能,同时自身可以添加独有的成员。
多态:有两方面的多态:一是操作名称的多态,即多个操作可以有同一个操作名,但参数必须不同;二是继承引起的多态,即通过父类引用指向子类对象,调用同一个方法时候可以产生不同的行为。安全性
Java在设计中就融入了强大的安全性策略。有以下三种安全机制:·语言设计特性(对数组的边界检测,无不受检查的类型转换,无指针算法等)
·访问控制机制,用于控制代码能够执行的操作(如文件访问、网络访问)
·代码签名,代码的作者使用标准加密算法认证Java代码。代码的使用者通过签名了解作者及代码是够被修改过。健壮性
Java编译器能够进行早期问题检测,校验器能够对类加载器传递给虚拟机的字节码文件进行校验,运行时的动态检测等。动态
Java程序的基本单元是类,有些类是自己编写的,有些类是从类库中加载的。这些类在运行时候被动态装载到内存,这就使得Java程序在分布环境中动态的维护程序及类库。
3. Java重要版本
3.1 版本1.1
1997年更新,增加了内部类。
内部类,顾名思义,定义在另一个类中的类。
优点:代码更加简洁。
原因:
- 可以访问该类定义所在作用域中的所有数据
- 对同一个包的其它类隐藏
- 当使用回调函数时,使用匿名内部类比较便捷
语法:
outerObject.new InnerClass(construction parameters)
例如:
Inner i=new Test().new Inner();
在实例方法中:Inner i=this.new Inner();
在外围类的作用域之外:Test.Inner i=new Test().new Inner();
(1) 内部类的机制
编译后的内部类使用$符号和外部类名连接而成的混合名字。例如一个内部类反编译后:
class Test16$Inner{
final Test16 this$0;
protected Test16$Inner(){
this$0 = Test16.this;
super();
}
}
注意:在内部类中创建了一个外部类的实例域,并且在构造方法中初始化为当前外围类的引用。
对于拥有私有域的外围类,编译器将为它创建一个静态方法以供内部类通过存储的外围类的引用获取其私有域。
(2) 局部内部类
特点:
局部内部类不能用public和private进行声明,因为它的作用域被限定在声明这个类的块中。
局部类不仅可以访问包含它们的内部类,还可以访问局部变量,但是局部变量必须设置为final。
机制:局部内部类在初始化期间会对要使用的局部变量进行拷贝,即创建自己的成员变量并使用局部变量的值初始化。因为局部变量在方法执行完毕后将被释放,而局部内部类可能仍在使用它,所以必须要拷贝。
局部内部类只能访问final修饰的局部变量:
public class Test3 {
public void show() {
//局部变量
final int i = 9;
// 局部内部类
class Inner {
public void print() {
System.out.println(i);
}
}
// i=100; 增加此代码编译将不能通过。
/*
* 为什么局部内部类只能访问final修饰的局部变量?
*
* Java 8版本之后的局部变量被内部类访问,即使没有声明final,也能通过编译,因为编译器会自动添加final修饰词。
* 但是局部内部类以后的任何对该局部变量的修改都会使编译无法通过。这里使用反证方法来证明。
* 假设被内部类使用的局部变量i可以修改并可以通过编译。那么在编译阶段:编译器生成单独的内部类Test3$1Inner,
* 它对局部变量i进行拷贝(在内部类中,为该变量建立数据域,在内部类的构造中使用局部变量值进行初始化),拷贝值为9。
* 在程序执行阶段,当进入show(),首先生成内部类Test3$1Inner的实例inner,然后执行i=100,然后调用inner.print(),这时该方法打印的i值为9,而我们
* 的本意是要打印修改后的i=100的值,这样就造成了数据不一致。所以局部内部类只能访问final修饰的局部变量。因为final修饰后的变量为常量,无法对其值
* 进行修改,这样才能保持数据一致,使程序正确的执行。
*/
Inner inner = new Inner();
inner.print();
}
public static void main(String[] args) {
new Test3().show();
}
}
(3) 匿名内部类
由于构造器的名字必须与类名相同,而匿名内部类没有类名,所以必须使用父类的构造器来初始化。
例如在接口回调中:
Runnable task=new Runnable(){
//重写的方法
public void run(){
System.out.println("a example");
}
}
这样大大简化了代码。
(4) 静态内部类
如果希望隐藏一个类,并且该类不需要访问外部类对象,应该创建静态内部类,这时内部类就不需要外部类的引用了。除此之外,静态内部类与其他内部类相同。
3.2 Java SE 5.0
3.2.1 泛型
(1)泛型的好处
使得程序具有更好的可读性和安全性。在泛型参数之前,泛型程序是用继承实现的,ArrayList类只维护一个Object引用数组,在获取一个值时必须进行强制类型转换。此外,没有错误检查,可以向数组中添加任何类对象。使用泛型后,ArrayList有一个类型参数来指示元素类型。错误类型的元素不会被添加,而取出一个值也不必进行强制类型转换。
在Java SE 7 后,构造函数中可以省略泛型类型,省略的类型可以从变量的类型推断得出(菱形推断)。
(2)泛型语法
类型变量限定:
限定类型必须在限定列表的第一个位置。
< T extends A,B >
A为限定类型,T必须是A的子类,A可以为B的子类。
< T extends A&B >
A,B为限定类型,T必须都满足A,B是父类。通配符类型:
< ? extends A >
不能用该类型的对象调用方法,但可以返回该类型的参数,此时该参数被转换为父类引用A。
该类型参数无法添加任何值,因为它告诉编译器的类型不确定,是一个范围,编译器无法确定这个对象的具体类型,为了类型安全,编译器不允许添加此类型的数据。但是返回类型总是一个A的实例(子类可以向上转型,但父类不确定类型情况下无法向下转型)。< ? super A > 超类型限定
带有超类型限定的通配符可以为方法提供参数,但不能使用返回值。
只能返回Object类型。而调用的时候可以用任意子类去调用这个方法。无限定通配符
< ? >通配符使用规律:
如果从一个数据类型里获取数据,使用 ? extends 通配符
如果把对象写入一个数据结构里,使用 ? super 通配符
如果既想存,又想取,那就别用通配符。
(3)泛型类和泛型方法
泛型类就是具有一个或多个类型变量的类。类型变量使用大写形式,且比较短。在Java库中,变量E表示集合的元素类型,K和V分别表示表的关键字和值的类型。
泛型方法的类型参数放在修饰符的后面,返回类型的前面。编译器会对具有泛型类型方法的可变参数列表打包然后找出共同的超类。
泛型方法的调用:Collections.binarySearch();
注:大多数情况下,编译器可以推断出泛型方法的类型参数,所以会省略泛型参数。
(4)泛型代码和虚拟机
虚拟机没有泛型类型对象,所有的对象都属于普通类。无论何时定义一个泛型类型,都自动提供一个原始类型。原始类型就是擦除类型参数后的泛型类型名(擦除类型变量并替换为限定名,没有限定的变量替换为Object)。例如把T全部替换为Object。
擦除泛型后的代码
泛型变量
擦除类型后,变量被替换为限定类型,例如一个没有限定类型的变量被替换为Object类型。
T t;
Object t;读取和存入t都要进行类型转换。
泛型方法
·同上,如果擦除返回类型参数,编译器将插入强制类型转换。
·泛型方法的翻译:子类:
public class DateTest extends Pair<Date> { public void setFirst(Date newFirst) { if (second.compareTo(newFirst) < 0) { this.first = newFirst; } } }
父类:
public class Pair<T> { T first; T second; public Pair(T first, T second) { this.first = first; this.second = second; } public void setFirst(T newFirst) { this.first = newFirst; } public static void main(String[] args) { Pair data = new DateTest(); data.second = new Date(); data.setFirst(new Date()); } }
编译后:父类擦除类型:T被Object替换。
public void setFirst(Object newFirst) { this.first = newFirst; }
子类增加了桥方法:
public void setFirst(Object newFirst){ setFirst(Date (Date)newFirst); }
原因:编译后,由于类型参数的擦除,子类中重写父类中的setFirst方法与父类中的方法参数一个是Date类型,一个是Object类型,变成两个方法,如果要使用多态时候就会出错。这就需要编译器合成的桥方法。
注意:
- 虚拟机中没有泛型,只有普通的类和方法。
- 所有的类型参数都用他们的限定类型替换。
- 桥方法被合成用来保持多态。
- 为保持类型安全性,必要时插入强制类型转换。
(5)约束与局限性
不能用基本类型实例化类型参数
Object不能存储double值,普通代码是编译器在编译期通过自动包装实现转换,而在类型参数中,类型擦除后含有类型参数类型的域比如T t;将被替换为Object t;此处存储基本类型就会出错。
例如:
T t;–>int t;–>(类型擦除后)–>Object t;
此处给t赋值后,类型擦除后Object也必须赋值为int类型数据,此处将出错。运行时类型查询只适用于原始类型
例如:
ArrayList<String> list1=new ArrayList<String>(); ArrayList<Integer> list2=new ArrayList<Integer>(); boolean flag=list1.getClass().equals(list2.getClass());//getClass总是返回原始类型ArrayList System.out.println(flag);//true //list instanceof ArrayList<String>//ERROR
不能创建参数化类型的数组
(6)泛型类型的继承规则
可以将一个参数化类型转换为一个原始类型。例如:
ArrayList<String> list1=new ArrayList<String>();
list1=(ArrayList)list1;
带有泛型参数的两个类之间没有任何关系。例如:Test< List >和Test< Collection > 之间没有关系。
3.2.2“for each”循环
for each是对标准Iterator用法的一个简化。增强程序的可读性和减少代码的出错机会。
例如:
//新建一个字符串数组
String[] weeks = { "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday", "Sunday" };
//使用for each循环遍历
for(String day:weeks)
System.out.println(day);
常见的错误是:ConcurrentModificationException,情形如使用for each语句对集合迭代,但是使用集合的删除方法删除元素。
3.2.3可变元参数
Varargs机制,允许定义能和多个实参相匹配的形参,编译器会把参数列表的数据包装成数组传递给方法体代码。
例如:
//1.对任意个整数求和的可变参数方法
public int sum(int...nums){
int sum = 0;
for(int a=0;a<nums.length;a++){
sum+=a;
}
return sum;
}
//2.Arrays中的静态可变参数方法,用数组初始化一个长度固定的列表。
List<Integer> aList=Arrays.asList(1,2,3,5,8);
注意:
- 只有最后一个形参才能被定义为可变参数。
- 编译器会把可便参转换为数组,所以不能为该类定义一个把可便参替换为数组的重载方法。
- 可变参数可能包括0个,但涉及到对可便参数数组索引操作的方法则会抛出异常。
- 自动拆箱装箱机制带来的问题,例如定义两个重载方法,形参类型一个为基本类型,一个为包装类型,那么编译出错。
3.2.4 自动装箱
8个基本类型:byte\char\short\int\float\long\double\boolean对应的包装类型是Byte\Character\Short\Integer\Float\Long\Double\Boolean
每个基本类型的包装类对象对应存储一个该基本类型的值。数值类型的六个类派生出共同的超类Number
例如:
//自动装箱
Integer i=Integer.valueOf(5);
//自动拆箱
int n=i.intValue();
3.2.5 静态导入
直接导入另一个类的常量或静态方法。但是某些情况下会导致可读性变差。
格式:
import static 包名.类名.常量或方法或*;
3.3 Java 7.0
时间:2011年
3.3.1 基于字符串的switch
用字符串来作为匹配switch语句的条件。
3.3.2 其它改进
(1) 二进制字面量
可以使用二进制数来表示一个整数类型。给二进制数字添加前缀0b或者0B。
例如:byte i=(byte)0b10101010;
(2)异常处理的改进
异常多重捕获
可以在单个catch块中处理多个同级别的异常,异常之间用“|”隔开。try-with-resources
该语句可以在try块中创建实现了Closeable接口的对象。之后无需显示关闭该资源,try块会自动关闭它。
例如:try(PrintStream ps=new PrintStream(System.out)){ ps.print("Hello"); }
4. Java有关术语
Java Development Kit|JDK|Java开发工具箱
Java Runtime Environment|JRE|Java的运行环境
Standard Edition|SE|用于桌面或简单的服务器应用的Java平台
Enterprise Edition|EE|用于复杂的服务器应用的Java平台
Micro Edition|ME|用于手机和其它小型设备的Java平台
NetBeans|Oracle的集成开发环境
Application Programming Interface|API|应用程序编程接口
5. JDK目录树
bin 编译器和工具
docs HTML格式的类库文档
include 用于编译本地方法的文件
jre Java运行环境文件
lib 类库文件
src 类库源文件