Java语言是一种分布式的面向对象语言,具有面向对象、平台无关性、简单性、解释执行、多线程、安全性等很多特点。
Java是一种面向对象的语言,它对对象中的类、对象、继承、封装、多态、接口以及包等均有很好的支持。为了简单起见,Java只支持类之间的单继承,但是可以使用接口来实现多继承。
平台无关性的具体表现在于,Java是Write Once, Run any Where
的语言。Java语言使用Java虚拟机机制屏蔽了具体平台的相关信息,使得Java语言编译的程序只需生成虚拟机上的目标代码(即将Java语言编译为.class
[字节码文件]),就可以在不同平台上不加修改地运行(JVM实现将字节码文件编译为机器码)
Java舍弃了很多C++中难以理解的特性,如操作符的重载和多继承等,而且Java语言不使用指针,加入了垃圾回收机制,解决了程序员需要管理内存的问题,使编程变得更加简单
Java程序在Java平台运行时会被转化为.class
文件字节码[.class
就是可以到处运行的文件],然后可以在有Java环境(JVM
)的操作系统上运行。在运行文件时,Java的解释器会对这些字节码进行解释执行,进行过程中需要加入的类在连接阶段被载入到运行环境中
Java支持多个线程同时执行,并提供多线程之间的同步机制。每一个线程都有自己的run()
方法,要执行的方法就写在run()
方法体内。
Java语言支持Internet应用的开发,而且Java有一个丰富的例程库,用于处理像**TCP/IP
和FTP
之类的TCP/IP
协议,包括URL
、URLConnetion
、Socket
**等。Java的RIM
机制也是开发分布式应用重要手段。
Java的强类型机制、异常处理、垃圾回收机制等都是Java健壮性的重要保证,并且Java采用的指针模型可以消除重写内存和损坏数据的可能性,异常机制也是健壮性的一大体现。
Java通常被用在网络环境中,为此,Java提供了一个安全机制以防止恶意代码的攻击。除了Java语言具有许多安全特性外,Java还对网络下载的类增加一个安全防范机制,分配不同的名字空间以防止替代本地的同名类,并包含安全管理机制。
JDK(Java SE Development Kit): Java标准的开发包,提供了编辑、运行Java程序所需要的各种工具和资源,包括了Java编译器、Java运行时环境,以及常用的Java类库
JRE(Java Runtime Environment):Java运行时环境,用于解释执行Java的字节码文件(即将
.class
文件翻译为机器码)。如果只是简单的运行Java程序,只安装JRE即可。
JVM(Java Virtual Mechinal), Java虚拟机,是JRE的一部分。它是Java实现跨平台的核心,负责解释执行字节码文件,是可运行Java字节码文件的虚拟计算机。所有平台上的JVM向编译器提供相同的接口,而编译器只需要面向虚拟机,生成虚拟机可识别的字节码。
JDK包括JRE,JRE包括JVM。Java编译器编译Java程序时,生成的是于平台无关的字节码文件,JVM是运行字节码文件的虚拟机。
在Java中,JVM可以理解的代码就叫做
字节码[.class文件]
(Java源代码经过编译器编译后的文件),它只面向虚拟机。Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点,因此Java运行时比较高效,而且字节码并不针对某一种特定机器,只要该机器上安装了JVM即可运行。
所谓跨平台性:是指Java语言编写的程序,一次编译后,可以在多个系统平台上运行。
实现原理:Java程序通过JVM在系统平台上运行,只要该系统安装了JVM即可运行Java程序。
到处运行的关键和前提就是JVM。因为第二次编译中JVM起着关键作用,在可以运行Java虚拟机的地方都内含着一个JVM操作系统,从而使Java提供了各种不同平台上的虚拟机制,因此实现了到处运行的效果**(Java不是编译机制,而是解释机制**)
从源代码到运行: 编码 => 编译 => 运行 (编译过程体现了Java跨平台的特点)
首先将Java源代码转化成
.class文件字节码
(JDK中的javac编译),.class文件
就是可以到处运行的文件。然后Java字节码会被转化为机器码,这是由JVM来执行的,即Java的第二次编译。
封装
利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能的隐藏内部的细节,只保留一些对外接口使之于外部发生联系。用户无需知道对象内部的细节,可以通过对象对外提供的接口来访问该对象。
优点
继承
继承实现了IS-A关系,例如Dog和Animal就是一种IS-A关系,因此Cat可以继承自Animal,从而获得Animal非private的属性和方法。
继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。
Dog可以当作Animal来用,也就是说可以使用Animal来引用Dog对象。父类引用指向子类对象称为向上转型
Eg: Animal dog = new Dog()
多态
编译时多态:主要指方法的重载
运行时多态:指程序中定义的对象引用所指向的具体类型在运行期间才确定
运行时多态有三个条件:
继承 覆盖(重写) 向上转型
两个整型(byte、short、int),在进行运算时会自动提升为int类型
+=
操作符会隐式进行自动类型转换,即将操作的结果类型自动转换为持有结果的类型,而a = a + b不会自动进行类型转换
byte a = 127;
byte b = 127;
b = a + b; //cannot convert from int to byte 因为此时b的类型仍为byte
b += a; //ok 此时为b的类型为int
再来看一个例子~,该代码是否有错
short s = 1;
s = s + 1;
有错误,short类型在进行运算时会自动提升为int类型,也就是说s + 1的结果为int类型,但是s仍为short类型
s += 1; //+=操作符会对右边的表达式结果强转匹配左边的数据类型 左边为short
自动装箱、拆箱实际上是一种语法糖。可以简单理解为Java平台为我们自动做了一些转换,保证不同的写法在运行时等价,它们发生在编译阶段,也就是生成的字节码是一致的。
装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型
原则上,建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合,创建10万个对象和10万个整数的开销可不是一个数量级的,不管是内存使用还是处理速度,光是对象头的空间占用就已经是数量级的差距了。
Integer i = 101; //自动装箱 调用对应包装类的valueOf方法 此处为Integer.valueOf();
int x = i; //自动拆箱 调用对应包装类的intValue方法 此处为x.intValue()
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
//试问i1 == i2 与 i3 == i4的结果 true | false
在提供valueOf方法创建Integer对象时,如果数值在[-128, 127]之间,便返回IntegerCache.cache中已存在的对象引用,否则则创建一个新的Integer对象
即
i1
与i2
返回的是同一个对象,而i3
与i4
则分别指向不同的对象
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
//同样的问题 i1 == i2 与 i3 == i4的结果 均为false
因为在某个范围内浮点数个数是没有限制的,即100.0后面还有若干位小数,故任意两个Double类型的对象均不等
为了让基本类型也具有对象的特征,就出现了包装类型。比如我们在使用集合类型Collection时就一定要使用包装类型而非基本类型。因为容器都是装Object的,这时就需要这些基本类型的包装类了。
- 基本数据类型与Java泛型不能配合使用
- 无法高效的表达数据,也不便于表达复杂的数据结构,如vector和tuple
Integer.valueOf() => IntegerCache.cache
Boolean 缓存了true/false对应实例,确切的说,只会返回两个常量实例Boolean.TRUE/FALSE
Short:与Integer相同,[-128, 127]
Bye:数值有限,全部被缓存
Character:缓存范围’\u0000’到’\u007F’
new
关键字,而包装类型需要用new
关键字来在堆中分配存储空间Collection
、Map
时会使用到String是Java语言非常基础和重要的类,提供了构造和管理字符串的各种基本逻辑。它是典型的Immutable
类,被声明为final class
,所有属性也都是final
的(即不可变)。由于它的不可变性,类似拼接、裁剪字符串等操作,都会产生新的String对象。由于字符串操作的普遍性,所以相关操作的效率往往对应用性能也有一定影响。
1.缓存hash值,因为String的hash值会经常用到,比如String做HashMap的key,那么String不可变保证hash值不可变,只需要计算一次,提高计算效率
2.String Pool 字符串常量池,一旦字符串String 被创建,下次创建相同的字符串就可以从常量池直接取,也只有当String是不可变才能这样做
3.安全String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。
4.线程安全 String是不可变的,保证了线程的安全。
StringBuffer是为解决String拼接产生太多对象问题而提供的一个类,我们可以用append
或者add
方法,把字符串添加到已有序列的末尾或指定位置。StringBuffer
本质是一个线程安全的可修改字符序列,它保证了线程安全,也随之带来了额外的性能开销,StringBuffer线程安全的实现是通过把各种修改数据的方法都加上synchonized
StringBuilder是Java1.5中新增的,在能力上它和StringBuffer
没有本质区别,但是他去掉了线程安全的部分,有效减小了开销,除非有线程安全的需要,它便是字符串拼接的首选
String
是Immutable
类的典型实现,原生的保证了基础线程安全,因为你无法对它内部数据进行任何修改,并且由于不可变,Immutable
对象在拷贝时不需要额外复制数据。
为了实现修改字符序列的目的,
StringBuffer
和StringBuilder
底层都是利用可修改的(char JDK9以后是byte)数组,二者都继承了AbstractStringBuilder
,里面包含了基本操作,区别仅在于最终的方法是否加了synchronized
final可以用来修饰类、方法、变量,分别有不同的意义,final修饰的类代表不可以继承扩展,final的变量是不可以改变的,final的方法也是不可以重写的。
finally则是Java保证重点代码一定要被执行的一种机制。我们可以使用tyr-finally
或者try-catch-finally
来进行类似JDBC关闭连接,保证unlock
锁等动作
finalize是基础类java.lang.Object的一个方法,他的设计目的是保证对象在被垃圾收集前完成特定资源的回收(现在已经不推荐使用)
finalize本是上成为了快速回收的阻碍者
public static final
(默认)default
方法,在JDK9中允许有private
普通方法public
、protected
;接口方法在JDK8之前只有public abstract
,在JDK8可以有default
方法,在JDK9中允许有private
方法接口是对行为的抽象,它是抽象方法的集合,利用接口可以达到API定义与实现分离的目的。
抽象类不能实例化,主要用于代码重用,大多抽取相关Java类的公用方法或者共同成员变量,然后通过继承的方式达到代码复用的目的
不同的引用类型,主要体现的是对象不同的可达性状态和对垃圾收集的影响
活着
,垃圾收集器就不会碰这种对象。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将(强)引用赋值为null,就可以被当垃圾收集了。反射机制是Java语言提供的一种基础功能,赋予程序在运行时自省(introspect
)的能力。通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至可以运行时调用。
动态代理时一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都是利用类似机制做的,比如面向切面编程(AOP)
实现动态代理的方式有很多,如JDK自身提供的动态代理,就是主要利用的上面提到的反射机制。还有更高性能的字节码操作机制(cglib)