Java 基础增强
jdk与jre
要想深入了解Java必须对JDK的组成, 本文对JDK6里的目录做了基本的介绍,主要还是讲解
了下JDK里的各种可执行程序或工具的用途
Java(TM) 有两个平台 JRE 运行平台,包括Java虚拟机,运行类库,java应用程序装载器。
JRE不是开发环境,所以不包括编译器,调试器,有需要这些请安装JDK(TM)
//---------------------------开发文件和目录------------------------------------------------
jdk1.8.0
___________|____________________
| | |
bin lib jre
| | __________|_____________________
java.exe tools.jar | |
javac.exe dt.jar bin lib
javap.exe _____|____ __________ ________|_______ ________ ________
javah.exe | | | | | | | |
javadoc.exe java.exe client server rt.jar ext security applet fonts
java.dll | | charsets.jar |
awt.dll jvm.dll jvm.dll localedata.jar
%JAVA_HOME% -- JDK的根目录,包含一些软件版权,声明,和自述文件,
同时包含归档了的Java平台源代码包src.zip
%JAVA_HOME%\bin -- JDK包含的一些开发工具执行文件
%JAVA_HOME%\jre\bin\client
包含 Java HotSpotTM Client Virtual Machine 要用的 DLL 文件
%JAVA_HOME%\jre\bin\server
包含 Java HotSpotTM Server Virtual Machine 要用的 DLL 文件
%JAVA_HOME%\lib -- Java开发工具要用的一些库文件,有包含了支持JDK工具的非核心类库tool.jar,
dt.jar 归档的 BeanInfo 文件
用于告诉IDE这样显示java组件怎样让开发者在自己的应用程序中用户化它们
%JAVA_HOME%\jre -- JDK使用的Java运行环境(JRE)的根目录,这个运行环境实现了Java平台
%JAVA_HOME%\jre\bin -- Java平台所要用的工具和库的可执行文件
这些可执行文件和 /jdk1.6.0/bin相同的。
//Java 启动器工具充当了应用程序启动器(覆盖了1.1版本的JDK推出的旧版本JRE工具)
这个路径不需要设置 PATH 环境变量
%JAVA_HOME%\jre\bin\client -- 包含Java Hotspot(Java性能引擎) 客户虚拟机要用的DLL文件
%JAVA_HOME%\jre\bin\server -- 包含Java Hotspot(Java性能引擎) 服务器虚拟机要用的DLL文件
%JAVA_HOME%\jre\lib -- JRE要用的代码库,属性设置,资源文件。
例如rt.jar Java 引导类库(java 核心APIRunTime类)
charsets.jar 字符转换类库
%JAVA_HOME%\jre\lib\ext -- 默认的Java平台扩展安装环境
包含localedata.jar 是 ava.text 和 java.util包要用到的地区数据
%JAVA_HOME%\jre\lib\security -- 包含安全管理文件,有安全规则(java.policy)
和安全属性文件(java.security)
%JAVA_HOME%\jre\lib\applet -- Java applets 要的Jar包,可以放到lib/applet/目录,
这样可以节省 applet 类装载器从本地文件系统装载 大的applets 所需的applet类时间
减少从网上下载具有相同的保护的时间。
%JAVA_HOME%\jre\lib\fonts 包含平台所需的TrueType字体文件
//不知道大家的版本有没有这个目录
-db目录 纯Java开发的数据可 Derby,是一个开源的100%Java开发的关系数据库
db
_________|__________
| | |
Demo Frameworks lib
-Dmeo 是Java Derby的例子程序
-Frameworks 提供数据库运行时需要的用到的shell脚本,包括Windows下的bat和Unix下的Ksh
包含 Java DB 的类库和 Sun Microsystems 的 Apache Derby 数据库技术的分发
有关 Java DB 的信息,请参见 http://developers.sun.com/prodtech/javadb/。
有关 Derby 的文档,请参见:http://db.apache.org/derby/manuals/index.html
//----------------------------附加的文件和目录--------------------------------------------
jdk
___________|__________ ___________
| | | |
demo include src.zip sample
___|___ _________ __________
| | | |
applets jfc jpda plugin
%JAVA_HOME%\src.zip -- 归档的Java源代码
%JAVA_HOME%\demo -- Java编程的例子
%JAVA_HOME%\demo\applets -- 网页Applets的例子
%JAVA_HOME%\demo\jfc -- Java 2D(TM)和JFC(基础图形类集合)\Swing 功能的例子
%JAVA_HOME%\demo\jpda -- 用Java平台Debugging的体系构架,包还有javadt 的 jdb 源代码,
具体内容可看jpda目录下的doc\index.html
%JAVA_HOME%\demo\jvmti -- java虚拟机tool interface (工具接口) 实例代码
%JAVA_HOME%\demo\plugin -- java 插件产品案例
%JAVA_HOME%\demo\nbproject -- JDK的 netbean工程示例
%JAVA_HOME%\demo\management -- 一些这样查看死锁线程(FullThreadDump ),
收集垃圾(VerboseGC)内存cpu使用状况了代码例子。详细可查看目录下的index.html
%JAVA_HOME%\sample -- 某些 Java API 的编程样例(带源代码)。
//有兴趣的可看看上面这些代码,很有用
%JAVA_HOME%\include -- C 语言头文件 支持 用Java本地接口和Java虚拟机接口 来本机代码编程
//-----------------------------------基本工具--------------------------------------------
这些工具是JDK的基础,用这些工具来编写应用程序。
javac.exe -- Java语言编译器
java.exe -- Java应用程序启动器,JDK 1.6版里同时用于开发和部署,
旧的部署启动器,jre,不在提供
javadoc.exe -- Java API 文档生成器
apt.exe -- java 注释处理器
appletviewer.exe -- java applet 小程序查看器
jar.exe -- java文件压缩打包工具
jdb.exe -- Java 调试器.
javah.exe -- C 头文件和stub生成器,用于写本地化方法,例如生产JNI样式的头文件
javap.exe -- class文件 反编译工具
extcheck.exe -- 用于检测jar包中的问题
//---------------------------------安全工具 -------------------------------------------
这些工具用于设置系统的安全规则和生产可以工作在远端的安全规则下的应用程序
keytool.exe -- 管理密钥库和证书.
jarsigner.exe -- 生产和校验JAR签名
policytool.exe -- 有用户界面的规则管理工具
kinit.exe.exe -- 用于获得和缓存网络认证协议Kerberos 票证的授予票证
klist.exe.exe -- 凭据高速缓存和密钥表中的 Kerberos 显示条目
ktab.exe.exe-- 密钥和证书管理工具
//--------------------------------Java国际化工具---------------------------------------
这些工具可以帮助你创建可本地化的应用程序
native2ascii -- 见文本转化为 Unicode Latin-1。//这个工具很有意思 ,大家可以看看这里
//http://java.sun.com/javase/6/docs/technotes/tools/windows/native2ascii.html
//--------------------------------远程方法调用工具-------------------------------------
这些工具可以帮助创建可以和web和网络交互的应用程序
rmic.exe -- 生成远程对象的stubs and skeletons(存根和框架)
rmid.exe -- Java 远程方法调用(RMI:Remote Method Invocation) 活化系统守护进程
rmiregistry.exe -- Java 远程对象注册表
serialver.exe -- 返回类的 serialVersionUID.
//------------------------------Java IDL and RMI-IIOP 工具-----------------------------
这些工具用于创建使用OMG-Standard IDL 和 CORBA/IIOP 的应用程序
tnameserv.exe -- Provides access to the naming service.
idlj.exe -- 生产映射到OMG IDL接口可以使Java应用程序使用CORBA的.java文件
orbd.exe -- 为客户可以在CORBA环境下透明的定位和调用服务器的稳定的对象提供支持
servertool.exe -- 为应用程序提供易于使用的接口用于注册,注销,启动,关闭服务器
//-------------------------------Java 部署工具------------------------------------------
pack200.exe -- 使用java gzip压缩工具将JAR文件转换为压缩的pack200文件,
生产打包文件是高度压缩的JAR包,可以直接部署,减少下载时间
unpack200.exe -- 解包pack200文件为JARs
//-------------------------------Java 插件工具------------------------------------------
htmlconverter.exe -- Java Plug-in HTML转换器 htmlconverter -gui 可以启动图形界面
//-------------------------------Java web 启动工具--------------------------------------
javaws.exe -- Java web 启动命令行工具
//-----------------------Java 故障检修,程序概要分析,监视和管理工具--------------------
jvisualvm.exe -- 一个图形化的Java虚拟机,不说了 大家研究一下就发现太酷了
// 啊这是想了解JVM的人的神器
//http://java.sun.com/javase/6/docs/technotes/guides/visualvm/index.html
jconsole.exe -- java监视台和管理控制台,图形界面的功能太强大了,
运行一下就知道 ,不想多说,看了就知道
//------------------------------Java web 服务工具----------------------------------
schemagen.exe -- Java构架的XML Schema生成器
wsgen.exe -- 生成 JAX-WS
wsimport.exe -- 生成 JAX-WS
xjc.exe -- 绑定编译器
//------------------------------监视工具-------------------------------------------
监视Java虚拟机的性能,不支持Windows 98 和Windows ME 平台
jps.exe -- JVM Process Status 进程状态工具。列出目标系统的HotSpot JJVM
jstat.exe -- 按照命令行的具体要求记录和收集一个JVM的性能数据
jstatd.exe -- JVM jstat 的守护进程
//-----------------------------故障检测和修理工具-----------------------------------
jinfo.exe -- 配置或打印某个Java进程VM flag
jhat.exe -- 堆储存查看器
jmap.exe -- Java内存图
jsadebugd.exe -- Java 的 Serviceability Agent Debug的守护进程
jstack.exe -- Java堆栈跟踪
//----------------------------Java脚本工具-----------------------------------------
jrunscript.exe -- 运行脚本
//工具都在JAVA_HOME\bin目录下,绝大部分工具都有-help命令行参数来提供帮助
- == 和 equals
-==
基本类型域比较,只比较值(short a=2;);用于对象比较时,比较的是两个对象引用是否指向同一个存储区域
int a = 1000; long b = 1000L;
System.out.println(a==b);//true
Integer c = 100,d=100,e=128,f=128;
System.out.println(c==d);//true,值在~128-127,则对象值是从Integer静态内部类的cache[]数组中取
System.out.println(e==f);//false,值范围超过~128-127,则Integer对象会new对象,详见下文33
-s.equals(t)
对象类型比较。多用于比较对象是否相等,s与t可以是字符串字面量,也可以是对象。Java中所有的equals方法都是超级父类Object中equals方法的重载,该方法内部使用 ==
符号来判断对象引用地址是否相等
public boolean equals(Object obj) {
return (this == obj);
}
- 空串和Null串
- 空串:是一个长度(0)和内容(空)的Java对象
- Null串:是一个String类型的变量的值,表示目前没有任何对象与该变量关联。
- StringBuffer和StringBuilder(jdk5.0)
- StringBuilder:用于单线程字符串拼接(拼接的效率高于创建对象),超过默认容量(16 characters)自动扩容,长度加16个字符
- String类的对象引用的值是所有线程共享的,是不变的
- StringBuffer与StringBuilder类利用append()方法为字符串添加内容,拼接字符串直接从常量池中取,节省空间,其中前者是线程安全的,利用
<+>
拼接字符串,如果所有字符串在一个单线程中编辑,应该用StringBuilder类,它是虽然多线程不安全,但不需要考虑线程安全时,它的性能更好。
- 对象与对象变量区分
- 对象是一个类的实例
- 对象变量仅仅是引用一个对象,它并不是对象,一个对象变量可以被赋予多个对象地址
- 码点与代码单元
char
数据类型是一个采用UTF-16编码表示Unicode码点的代码单元,Java字符串是由char值序列组成。- 大多数Unicode码点用一个char代码单元就能表示,但辅码需要2个代码单元表示(即2个char)
- System.out.printf(%8.2f,x)
- %后的值将由参数x替换。将以8个字符宽度和小数点后两位小数的精度,来打印x
- 用于printf的转换符(参考P60)
符号 | 类型 | 例子 |
---|---|---|
d | 十进制整数 | 110 |
s | 字符串 | Hello |
c | 字符 | H |
b | 布尔 | True |
h | 散列码 | 42628b2 |
f | 定点符点数 | 1.12 |
- break,continue及return
break
跳出循环最里层循环
continue
结束循环体中的本次循环,继续下一个循环迭代return
结束方法
- OOP(面向对象编程)的特点
encapsulation
封装- 封装的关键要求是不能让类中的方法直接访问其他类的实例域(属性/成员变量),程序仅能通过对象的方法与对象数据进行交互(“黑盒”)
inheritance
继承。- 继承是子类对超类的扩展
- 子类会继承所有父类的非private属性和方法
- 子类不能重写父类final修饰的方法
- final修饰的类不能被继承
多态
在Java中,对象变量是多态的。Override
,重写式多态(运行时多态,动态分派),通过动态绑定实现,是指在执行期间判断对象的实际类型
,根据其实际类型调用相应方法,这种多态是通过函数重写以及向上转型实现。因为
public class DynamicDispatch {
static abstract class Human {
protected abstract void sayHello();
}
static class Man extends Human {
@Override
protected void sayHello() {
System.out.println("man say hello");
}
}
static class Woman extends Human {
@Override
protected void sayHello() {
System.out.println("woman say hello");
}
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
man.sayHello();//"man sayHello"
woman.sayHello();//"woman sayHello"
man = new Woman();
man.sayHello();//"woman sayHello"
}
}
- OverLoad,重载式多态(编译时多态,静态分派),重载即方法名相同,参数不同。编译器在重载时是根据参数的`静态类型` 而不是`实际类型` 作为具体方法调用的判断依据。下例中静态类型为Human,human的实际类型是Man,但javac编译器在编译器并不知道,方法的重载只是编译期确定的,例子中可以使用强转调用Man的方法,`sy.sayHello((Man)human)`
public class StaticDispatch {
static abstract class Human {
}
static class Man extends Human {
}
public void sayHello(Human guy) {
System.out.println("hello,guy!");
}
public void sayHello(Man guy) {
System.out.println("hello,gentleman!");
}
public static void main(String[] args) {
Human man = new Man();
StaticDispatch sr = new StaticDispatch();
sr.sayHello(man);//"hello,guy!"
}
}
- 向上转型。父类对象变量的引用指向子类对象,即通过`=` 将子类对象值的引用赋值给父类对象变量
- 向下转型。需要强制转换,并且转换对象的对象类型必须与父类对象变量的引用指向的对象一致。不能把一个父类对象转化为子类对象
- 类型转换的原因:在暂时忽略对象的实际类型后,使用对象的全部功能
扩展
- Java中方法的调用的过程是对应的方法栈桢在虚拟机栈进栈和出栈的过程,其中
每个栈桢都包含一个指向运行时常量池中该栈桢所属的方法的引用
,这些引用一部分会在类加载阶段就被转化为直接引用(非虚方法),这种转化称为静态解析
;另一部分在运行期间才会被转化为直接引用,称为动态连接
- 编译期可知,运行期不可变。Java语言里可以在编译期就确定唯一的方法有,静态方法、私有方法、实例构造器、父类方法4种,再加上被final修饰的方法(尽管它使用invokevirtual指令调用),这些方法被称为
非虚方法
,这些方法在类加载的解析
阶段就会把符号引用转变为明确的直接引用,不必延迟到运行期再执行
- Java中方法的调用的过程是对应的方法栈桢在虚拟机栈进栈和出栈的过程,其中
- 更改器方法与访问器方法
- 更改器方法(mutator method),会改变对象的属性(实例域),例如,属性的set()方法。类似这样的方法会破坏封装性
- 访问器方法(accessor method),只访问对象而不修改对象的方法。例如,属性的get()方法,可以参考源码,String类,LocalDate类中的方法。
final
修饰类,类不能被继承,类中的方法自动地会转变为final修饰,这样的类是不可变类
修饰属性,初始化必须显示赋值
修饰方法,方法不能重写
修饰对象变量,该变量不能再引用其他对象,但当前引用的对象的属性可以更改
生命:用final修饰类或方法的目的是使其在子类中不会被改变语义
static
- 静态域:归类所有。每个实例对象都有自己一份实例域,并共享静态域(static修饰的属性)
- 静态方法:归类所有,类名+静态方法名调用。不建议用对象调用,静态方法只能访问静态域。
- 情景1:一种方法不需要访问对象的状态,方法所需参数都是显示提供。eg:Math.pow()
- 情景2:一种方法只需要访问类的静态域
- 参数传递
Java中采用的是按值传递。
- 对于基本类型(数字/布尔型)成员变量,方法中引用基本类型实例值,方法内部对参数进行更改,不会影响方法外部该参数的值,实际方法调用的参数是实例值的拷贝
- 对于引用类型的参数,方法内部是对该对象变量引用参数的拷贝,原始对象变量与拷贝后的变量共同指向同一个对象实例,所以方法内部对对象实例进行更改后,原始对象变量也改变了,表面上看想是引用传递。实际拷贝的是指向对象实例的原始对象变量值的拷贝。
简单说
- 一个方法不能改变基本类型的参数
- 一个方法可以改变一个对象参数的状态
- 一个方法不能让对象参数引用另一个对象
- 类构造建议
- 一定要保证数据私有(保证封装性)
- 一定要对数据初始化(Java会对对象的实例域初始化,但不会对局部变量初始化)
- 不要在类中过多使用基本类型(用其他类代替过多基本类型使用,增强可读性且易于修改)
- 不是所有的域/对象都需要域更改器方法和访问器方法
- 将职责过多的类进行分解
- 优先使用不可变类
- 类名和方法能展示它的职责
- this与super
this
当前对象的引用- 情景1:引用隐式参数(即当前对象,而显示参数指的是方法参数),this.methodName()当前对象对方法的调用
- 情景2:调用该类的其他构造器
super
只是指示编译器调用超类方法的关键字- 情景1:调用超类的方法
- 情景2:调用超类的构造器
注意:调用构造器的语句只能位于另一构造器的第一行
- 方法调用过程
- 编译器查看方法的声明类型和方法名
- 接下来编译器查看调用方法时提供的参数类型
- 如果是priviate/static/final类型方法,构造器将会准确知道调用哪个方法,这种调用也称静态绑定(static binding),与此对应,调用的方法依赖于隐式参数的实际类型,并且在运行时实现动态绑定
- 当程序运行,并且采用动态绑定的方法,虚拟机会调用与所引用对象的实际类型最合适的那个类的方法,如果没找到,就到父类查找(显示声明super(),则会直接在超类搜寻)。
- 虚拟机中通过参数类型和返回类型确定一个方法
- 数组的capacity与size
capacity
表示数组有保存多少元素的能力size
表示数组实际有多少元素trimToSize(),当确定数组容量不会再变化,调用该方法会将数组空间缩减至当前尺寸。
- Object类
Object类是所有类的始祖,如果没有明确指明一个类的超类,那么Object类就会被认为是这个类的超类
- 可以使用Object类对象引用所有类型的对象
- Object类的变量只能作为各种值的通用持有者,要对其中类型进行操作,必须知道对象的原始类型,并进行强制类型转换,才能使用自己定义的方法
- 在Java中,只有基本类型(数字,字符,布尔)不是对象,所有的数组类型(基本数组,对象数组)都扩展了Object类
equals()
:比较两个对象是否相等- 如果子类拥有自己相等的概念,则对称性需求将采用getClass()检测
- 如果由超类决定相等的概念,则使用instanceof()检测,这样可以在不同子类对象间进行相等比较
- Object类的本身的equals方法通过判断,对象的hashCode是否相同即地址是否相同,来判断两对象是否相同;如果子类
重写equals方法,建立自己的相等概念,最好重写hashCode方法
,否则子类对象默认还是使用的Object父类的方法
比较的是通过Object的hashcode算法算出的对象的地址
hashCode()
- 由字面量内容导出,hashCode码是一样的
String s = "OK";String t = new String("OK");
//true,因为String类 重写 了equals方法及hashCode方法,相同的方法对相同的字面量算出的hashCode值是一致的
//注意,仅仅是hashCode值相等,s 与 t 对象不相等,new 关键字会在内存中开辟新空间
System.out.println(s.hashCode()==t.hashCode());
- 由Object类的默认hashCode方法导出,是对象的存储地址
StringBuilder ss = new StringBuilder(s);
StringBuilder st = new StringBuilder(t);
//false,StringBuilder使用的是Object的 equals和hashCode方法,返回的是对象的存储地址
//s 与 t 对象变量指向内存中的地址本身就不相同
System.out.println(ss.hashCode()==st.hashCode());
- 相等的对象
hashCode值
一定相等;而hashCode值
相等,对象不一定相等(哈希冲突)
toString
- 表示返回对象值的字符串。
- 若是数组调用,则会返回hash值,因为数组继承了Object类;想打印数组可以使用Arrays.toString()
- 数组
数组类默认扩展了Object类,没有泛型类时,数组类的get()方法只能返回Oject,因此get()方法调用者必须对返回值进行强制类型转换
数组类继承了Object类的toString()方法,正确打印数组是调用静态Arrays.toString()打印
数组排序:对象数组类实现comparable接口,可以调用Arrays.sort()方法
Java中数组有一个限制,无法构造泛型类数组,可以用Lambda表达式做到
- 自动装箱/拆箱
装箱会造成性能浪费。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值
char
boolean
byte
=< 127,short
和int
类型介于-128~127之间会被自动装箱到固定对象中基本类型是只是一个字面量,在一个类中一但被创建只会实例化一次。但是如果是包装类型,与基本类型进行加减运算时,会先自动拆箱进行运算,然后将结果进行装箱(如,Integer.valueOf(sum)),装箱返返回的是一个创建的实例,多次运算会返回多个实例,性能浪费。
Java是面向对象的语言,包装类型是一个对象,可以调用方法,而基本类型仅仅是字面量,无法进行其他操作
字符串转变为整型
- int x=Integer.parseInt(s)
字符串转变为包装类型(Integer)
- Integer x=Integer.valueOf(s)
- 接口与抽象类
共同特点,抽取重复代码,设置约束,接口是比抽象类更抽象的抽象类;不同点,抽象类只能单继承,一个类可以实现多个接口
接口特点
- 静态方法。在Java SE8 中,允许定义静态方法
- 默认方法。可以为接口提供一个default修饰的默认方法,因为在接口中定义的是完整的方法,所以实现接口的类可以直接调用。接口默认方法与超类冲突/接口冲突解决方法如下
- 超类优先,与接口中同名同参数的默认方法会被忽略
- 接口冲突,必须指定使用的那个接口方法,并且覆盖该方法
- 注意,不能重新定义Object类中的方法为默认方法,类优先规则会使它无效
- 接口中的普通方法默认都是由public abstract修饰(javap 指令反编译查看);若定义了static则是public static 修饰;接口中不允许定义final修饰的方法
- 实现类必须实现不含
static
default
修饰的方法
- 实现类必须实现不含
- Comparable(java.lang)与Comparator(java.util)
Comparable接口,
字典顺序排序
,需要在类内部
重写compareTo()方法- 实现了 Comparable 接口的 List 或则数组可以使用 Collections.sort() 或者 Arrays.sort() 方法进行排序
- 实现了 Comparable 接口的对象才能够直接被用作 SortedMap (SortedSet) 的 key,要不然得在外边指定 Comparator 排序规则。
- 一般自定义类想要进行比较时,除了实现
Comparable
外,还需要重写equals与hashCode
方法
Comparator接口,
自定义排序规则
,无法修改实体类时,直接在调用时创建并自定义比较方法
,使用方式如下- 创建一个 Comparator 接口的实现类,并赋值给一个对象
- 在 compare 方法中针对自定义类写排序规则
- 将 Comparator 对象作为参数传递给 排序类的某个方法
- 向排序类中添加 compare 方法中使用的自定义类
22.lambda(P235)
格式:参数,箭头(->),表达式
- 编译器可以推导出参数类型时,可以不写参数类型
- 无需指定方法返回类型,它会根据上下文推导。注意,同一个lambda方法在不同地方调用时返回类型必须统一
函数式接口:对于只有一个抽象方法的接口,当需要这种接口对象时就可以提供一个lambda表达式。这种接口称函数式接口,例如 comparable 接口
方法的引用:对象或类名::方法名
Object::instanceMethod
例:x->System.out.println(x) 等价于 System.out::println
Class::staticMethod
例:(x,y)->Math.pow(x,y) 等价于 Math::pow
Class::instanceMethod(第一个参数成为方法的调用者)
例:(x,y)->x.compareTo(y) 等价于 String::compareTo
构造器的引用:类名::new
lambda表达式引用的变量只能是不会再改变的变量
lambda表达式的优点
- 1.延迟执行(deferred execution)
- 克隆
copy
对对象变量值copy,两个变量指向同一对象Object.clone()
是浅克隆,只能克隆一个对象中引用的是不变类型的对象,如String,LocalDate类,无论自身调用重写clone()方法还是子类调用Object类的clone()方法,都必须实现Cloneable
接口,Object类本身没有实现该接口- x.clone!=x;//true
Cloneable
是一个标记接口,内部没有方法,标记接口的唯一作用就是在类型查询时使用instanceOf
所有的数组对象都实现了Cloneable接口
使用,实现Cloneable接口,重写clone()方法,方法使用
throws
显示声明异常。jdk1.4以后可以自定义返回类型,之前版本只能使用Object返回类型- 浅拷贝。需要拷贝的对象内容是基本类型,或者引用类型的对象是
final
修饰保证其不变性; - 深拷贝。需要拷贝的对象包含引用类型且是可变的,要求引用类型的对象必须实现Cloneable接口,然后再拷贝时需要自己
单独
调用clone()方法。
- 浅拷贝。需要拷贝的对象内容是基本类型,或者引用类型的对象是
- Proxy
- 代理类是在程序运行过程中创建的,一旦被创建就变成了常规类,与虚拟机中其他类没有区别。
- 所有代理类都扩展于Proxy类。一个代理类只有一个实例域——调用处理器,它是定义在超类Proxy中。为了履行代理对象的职责,所需要的任何附加数据都必须存储在调用处理器中。
- 所有的代理类都重写了Object的equals(),hashCode(),toString()方法,如同所有代理方法一样,这些方法仅仅调用了处理器的invoke
- 调用处理器invocationhandle是实现了InvocationHandle接口的类对象,在这个接口中只有一个方法,无论何时调用代理对象,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始调用参数。
Object invoke(Object proxy,Method method,Object[]args)
创建代理对象,需要使用Proxy类的newProxyInstance方法
newProxyInstance(ClassLoader classloader,Class<?>[]class,InvocationHandle invocationhandle)
- 代理类一定是final和public。
- 内部类
特点
- 只有内部类才能定义为私有;内部类可以访问外部类的数据域(成员变量)
- 内部类中声明的所有静态域(成员变量)都必须是final,保证每个外部对象所拥有的内部类实例是唯一的,且内部类不能有static方法,如果设定了静态方法,则只能访问外部类的静态域和静态方法,得不偿失。
- 内部类默认有个指向外部类的引用(OuterClass.this),可以使用如下方法编写内部类的构造器
outObject.new InnerClass(construction parameter)
//创建内部类对象,this代表外部类 this.new InnerClass
局部内部类
- 不能用public和private修饰,保证局部内部类对外部类不可见
- 不仅能访问包含局部内部类的外部类,还能访问final修饰的局部变量
匿名内部类
- 需要一个对象而不需要对象名
//常归类 Person person=new Person("Tom");
//匿名内部类 Person person=new Person("Cat"){……}
可以在内部类中定义自己的方法,代码更简洁
//常规构造数组对象 ArrayList<String> list=new ArrayList<>(); list.add("Tom"); list.add("Lily"); invite(list);
//匿名内部类构造数组对象 invite(new ArrayList<String>(){{add("Tom");add("Lily");}});
此处外围第一组{}建立了ArrayList的一个匿名子类,第二组内层括号则是一个对象构造块
静态内部类(看作类的成员变量)
参考Arrays.AsList(T... a)方法
- 在内部类不需要访问外围对象时使用,用static
- 与常规内部类不同,静态内部类可以有静态域和静态方法
- 声明在接口中的内部类自动成为public和static
- 静态内部类除了没有指向外部的引用外,与其他内部类一样的功能。
- 异常
Error
:由于运行时系统内部的错误或资源耗尽错误。(unchecked,非受检查异常)Exception
RuntimeException
:程序错误导致(unchecked,非受检查异常)IOException
等其他异常(checked,受检查异常),需要处理
RuntimeException | 非派生于RuntimeException |
---|---|
类型转换异常 | 试图在文件尾部读取数据 |
数组越界 | 试图打开一个不存在的文件 |
访问null指针 | 由给定字符串查找的Class类对象不存在 |
处理异常:抛出、捕获、自定义异常类
- 抛出:方法声明时使用throws声明异常类型
- 自定义异常类:派生于Exception或它的子类,定义的类应该包含两个构造器,
默认构造器
和带有详细描述信息的构造器
Throwable();//默认构造器 Throwable(String message);//带有详细描述信息的构造器 String getMessage();//获取Throwable对象的详细信息
异常注意事项
- 子类异常不能比父类异常范围还大
- 不能用异常代替简单测试(处理异常很耗时)
- 不要过分地细化异常
- 利用异常结构
- 不要压制异常
- 早抛出,晚捕获
- 断言(assert)
- 断言机制允许在测试期间向代码插入一些检查语句,当代码发布时,这些语句会被自动地移走
- Java语言assert关键字有两种形式
assert 条件;
assert 条件:表达式;
这两种形式都会对条件进行检测,如果结果为false,则会抛出一个AssertionEroor异常,在第二种形式中,表达式将被传入AssertionError构造器中,并转化成一个消息字符串(表达式的唯一目的是产生一个消息字符串,输出控制台,AssertionErrot对象并不会存储它)
- 泛型
辅助理解泛型的几个问题
- 泛型 erased(擦除)对类的影响(从泛型成员变量/成员方法,以及继承泛型类的角度分析利弊)
- 泛型使用规范
- 泛型优点
- 泛型生命周期,即是否存在于虚拟机
Java类库中,使用变量E表示集合,K和V分别表示表的关键字与值的类型,T(或U或S)可以表示“任意”类型
泛型作用在类上:class ClassName <T>
泛型用在方法上:
MethodName()
:<T>T对泛型变量进行限定:
public static <T extends Comparable&Serializable>T methodName(){}
T是实现了Comparable和Serializable接口的泛型类
- 用
&
对类型变量进行多个限定,而,
用来分隔类型变量。 - 用extends关键字的原因:T和绑定类型(BoundType)既可以是类,也可以是接口,用extends更接近于子类(设计师也没打算再添加关键字)。
- 类型擦除。无论何时定义一个泛型类型,都自动提供了一个相应的原始类型(raw type),原始类型的名字就是删去类型参数后的泛型类型名。例如TimeTest,原始类型为TimeTest。擦除(erased) 类型变量, 并替换为限定类型 (无限定的变量用 Object),例如
- 用
泛型转换事实
- 虚拟机中没有泛型,只有普通的类和方法
- 编译器编译时会擦除泛型变量并用其原始类型代替(第一个限定类型)
- 为保持类型安全性,必要时进行强制类型转换
泛型使用注意事项
- 不能用基本类型对泛型变量限定(即泛型只能绑定到引用类型)
- 运行时类型(虚拟机)查询只适用于原始类型,虚拟机中没有泛型,只有普通的类和方法
- 不能实例化类型变量,如
new T(...)
new T[...]
T.class
这样的类型变量是不合法的,Java 8及以后可以用lambda构造器引用来应对这个问题 - 不能构造泛型数组
- 不能在静态域或方法中引用泛型变量
- 不能抛出或捕获泛型类的实例
- 可以消除受检查异常
- 注意擦除后的冲突
永远可以将参数化类型转化为转化为一个原始类型
继承相关。虽然Manager类继承于Employee类,但两个类作为泛型限定类型时,这样的泛型变量将失去继承关系,是两个无关的类。
通配符
?
虚拟机中通过参数类型和返回类型确定一个方法
通配符子类。Pass<? extends Employee>,
?
此处代表类型是Employee的子类型//编译器不知道应该传入的参数是Employee的哪一个子类型,编译不通过 void setXXX(? extends Employee) //将getXXX()返回值赋给Employee的一个引用,编译通过。因为普通类中可以将子类对象值赋给父类对象变量的一个引用(多态) ? extends Employee getXXX()
通配符超类限定。Pass<? super Mannger>,意味着泛型类型变量只能是Manager类或某个子类对象(如Executive),并且此时可以为方法提供参数,但不能使用返回值,此外如果调用getXXX()方法,只能把它的值赋给一个Object
//传入参数是Manager类型,编译通过 void setXXX(? super Manager); //不能保证返回值类型,可能是Manager,也可能是Object类,所以编译器会把返回值赋值给Object,所以不能直接使用它的返回值 ? super Manager getXXX();
无限定通配符。Pass<?>
//返回对象是Object类型 ? getXXX(); //方法不能调用(也不能用Object调用),但是可以调用setXXX(null) void setXXX(?);
应用,测试Pass是否包含一个null引用,不需要实际类型
public static boolean hasNull(Pass<?> p) { return p.getXXX()==null; }
Pass<?>与Pass类的本质不同在于:Pass类可以用任意Object对象调用原始Pass类的setObject方法
综上,带有超类型限定的通配符可以向对象写入,带有子类型限定的通配符可以从泛型对象读取
- 数据域初始化
- 声明时显示指定
- 构造器中指定
- 初始化块中指定
- 调用构造器初始化数据域的顺序
- 所有数据域被初始化为默认值
- 按照在类中声明的次序,依次执行所有域初始化语句和初始化块
- 如果构造器第一行调用了第二个构造器,则执行第二个构造器主体
- 执行这个构造器的主体
每个类中显示声明无参构造器的意义
- 子类继承可以构造器中调用父类的构造器,
super()
,注意this()
是调用本类的其它构造器 - 没有显示指定构造器时,编译器会自动的创造一个公有的无参的构造器,即使是抽象类也能被实例化(抽象类例化并没有意义)。避免抽象类实例化或阻止子类调用,父类将无参构造器显h私有即可。
- 子类继承可以构造器中调用父类的构造器,
如何判断一个类是否是基本类型,是否是对象实例或子类,是否是数组类型
- Class 对象判断
- 判断是否是基本类型. Xxx.class.isPrimitive():boolean。
- 判断是否是某种基本类型. String.class.isInstance(str):boolean
- 判断是否是对象实例或子类. Xxx.class.isInstance(obj):boolean
- 判断是否是自身类的实例. 自身类.class.isAssignableFrom(自身类或子类.class)
- 判断是否是数组类型. Xxx.class.isArray():boolean
- 获取数组中元素类型. int []ai={1,2,3};ai.getClass.getCompontenType()
- 实例对象判断
- instanceOf
- Class 对象判断
垃圾回收(内存泄漏)
- 前提:对象,对象变量,对象引用概念 见上文
栈(过期引用):栈内部维持着对过期对象(弹出栈的对象)的引用(unintentional object retention,无意识的对象保持)
- 改进:弹栈实际从数组中将一个对象引用放到数组外,但是实际这个在内存中的对象引用还是指向堆中的数据,程序运行久了内存会因为栈中过期对象的增加而不够用(内存泄漏),最终程序终止。每次弹栈后手动将内存中该对象的引用指向
null
则Java的垃圾回收机制会回收内存中这个已弹出栈的对象引用
- 改进:弹栈实际从数组中将一个对象引用放到数组外,但是实际这个在内存中的对象引用还是指向堆中的数据,程序运行久了内存会因为栈中过期对象的增加而不够用(内存泄漏),最终程序终止。每次弹栈后手动将内存中该对象的引用指向
缓存(对象引用放到缓存中):随着时间的推移,缓存中对象引用越来越多,需要考虑缓存内容是否有价值及正确设置缓存声生命周期
- 原理:缓存中存放的是个WeakHashMap,缓存的生命周期是由该键的外部引用决定而不是由键的值决定。
- 改进:
方案1
,外部不对缓存对象引用引用,WeakHashMap会自动清理无用的引用;方案2
,缓存用LinkedHashMap表示,当给缓存添加新条目时会清理无用的引用( LinkedHashMap 类利用它的removeEldestEntry);方案3
,清理工作由后台线程(可能是 Timer 或者 ScheduledThreadPoolExecutor )来完成;方案4
,复杂的缓存使用java.lang.ref
来处理
监听器和回调
- 如果你实现了一个API,客户端在这个API中注册回调,却没有显式地取消注册,那么除非你采取某些动作,否则它们就会积聚。确保回调立即被当做垃圾回收的最佳方法是只保存它们的弱引用(weak reference),例如,只将它们保存成 WeakHashMap 中的键。
排序
- 底层:
Comparable.compareTo(T o)
与Comparator.compare(T o1,T o2)
两个接口都是函数式接口
简单排序。实现
Comparable
接口并重写compareTo(T o)方法。按字典顺序排序自定义排序。实现
Comparator
接口,并重写接口中非static
修饰和非default
访问权限的compare(T o1,T o2)
方法为数组排序。
- Arrays.sort(Object[] arrays,Comparator<? super T> c),arrays是待排序数组对象,c是Comparator接口的实例(该类中自定义了排序规则),该接口中的静态方法
comparing()
底层使用的是函数式接口(Comparable),所以提供一个lambda表达式就可以返回一个Comparable接的实例,使用Java8的lambda
表达式优雅排序实例
Arrays.sort(people, Comparator.comparing(Person::getName).thenComparing(Person::getAge).thenComparing(Person::getSalary));
- Arrays.sort(int[] a),数组a可以是所有基本类型的数组对象,
- Arrays.sort(Object[] arrays,Comparator<? super T> c),arrays是待排序数组对象,c是Comparator接口的实例(该类中自定义了排序规则),该接口中的静态方法
集合排序。无论是ArrayList还是Map排序底层都调用的是Arrays.sort()方法,集合工具类Collections调用的也是Arrays.sort()方法。
Arrays.sort()方法底层对对象数组排序规则是基于
Comparator
接口定义的;对于基本类型的数组,如int[],byte[]等数组,使用的是Java封装的优化的快排,归并
等方法局部变量与成员变量
- 局部变量保存在栈上,属于方法所在的一个线程;成员变量保存在堆中,为所有线程所共享
- Java方法参数是按
值传递
,方法内部调用成员变量(其实是对原来成员变量进行的一个copy,它们指向堆中同一个值),然后对copy的成员变量进行更改,则方法外部对象的成员变量也会更改,因为类的成员变量和方法中copy的成员变量指向的是同一个值。
- Java数值类型缓存(Byte,Short,Integer,Long)
- 关键字:
static class XxxCache{}
static{}
valueOf()
- 未使用 new 创建整型包装类对象时,且值范围在 -128~127之间,类初始化时每个包装类都会有一个缓存静态内部类,通过静态内部类中的静态代码块为内部类的静态 catch[] 数组赋值,cache数组索引范围为 0~256,索引下标对应的值范围为 -128~127,当客户端的值在 -128~127 时,会从缓存中取,超过这个范围,则会new 一个包装类对象
- 关键字:
Java 基础增强的更多相关文章
- java基础增强
Eclipse使用: java Compile配置的是java编译环境 java Build path配置的是java运行环境 运行环境的版本必须高于编译环境的版本.否则报错 工程上 右键--prop ...
- Java基础——增强for循环
java1.5版本引入了一个增强for循环,基本原理和for循环类似. 语法声明:for(表达式:条件表达式) 举例:for (String str : set) 解释:set代表set集合,str代 ...
- 【Java EE 学习 31】【JavaScript基础增强】【Ajax基础】【Json基础】
一.JavaScript基础增强 1.弹窗 (1)使用window对象的showModelDialog方法和showModelessDialog方法分别可以弹出模式窗口和非模式窗口,但是只能在IE中使 ...
- java基础-迭代器(Iterator)与增强for循环
java基础-迭代器(Iterator)与增强for循环 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Iterator迭代器概述 Java中提供了很多个集合,它们在存储元素时 ...
- 【JAVA面试题系列一】面试题总汇--JAVA基础部分
JAVA基础 基础部分的顺序: 基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法 线程的语法,集合的语法,io 的语法,虚拟机方面的语法 每天几道,持续更新!! 1.一个". ...
- Java基础知识【上】(转载)
http://blog.csdn.net/silentbalanceyh/article/details/4608272 (最终还是决定重新写一份Java基础相关的内容,原来因为在写这一个章节的时候没 ...
- 黑马程序员----java基础笔记中(毕向东)
<p>------<a href="http://www.itheima.com" target="blank">Java培训.Andr ...
- 黑马程序员----java基础笔记上(毕向东)
------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 笔记一共记录了毕向东的java基础的25天课程,分上.中.下 本片为上篇,涵盖前10天课程 1. ...
- JAVA面试精选【Java基础第一部分】
这个系列面试题主要目的是帮助你拿轻松到offer,同时还能开个好价钱.只要能够搞明白这个系列的绝大多数题目,在面试过程中,你就能轻轻松松的把面试官给忽悠了.对于那些正打算找工作JAVA软件开发工作的童 ...
随机推荐
- 使用tomcat运行时提示some characters cannot be mapped using iso-8859-1 character encoding异常
今天第一次使用java进行jsp项目搭建,也是第一次使用tomcat.tomcat是运行java web的一个小型服务器,属于Apache的一个开源免费的服务. 在运行web 的时候,我们就要先配置好 ...
- 【5min+】更好的选项实践。.Net Core中的IOptions
系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...
- django之 F与Q查询
F与Q查询 F查询 why?
- [Docker03] Deploy LNMP on Docker
Deploy MYSQL docker pull mysql 挂载卷保存数据文件 mkdir -p /mysql/data chmod -p 777 /mysql/data MySQL使用过程中的环境 ...
- nmap端口扫描工具安装和使用方法
nmap(Network Mapper)是一款开源免费的针对大型网络的端口扫描工具,nmap可以检测目标主机是否在线.主机端口开放情况.检测主机运行的服务类型及版本信息.检测操作系统与设备类型等信息. ...
- spring 请求静态资源文件
在springMVC项目中使用restful风格写,需要到web.xml配置全拦截. <servlet> <servlet-name>springmvc</ ...
- Centos单机部署Elasticsearch7.2集群
配置node0 # ======================== Elasticsearch Configuration ========================= # # NOTE: E ...
- PHP - json_decode returns NULL的解决办法
碰到了PHP json_decode returns NULL, 肿么办? 1. google 一下, 关键字:PHP json_decode NULL 首先你能看到我这个这个帖子:) http:// ...
- Mysql 随笔记录
Soundex 声音相似的 select * from demos where Soundex('title') = Soundex('标示'); Concat 拼接语句 select concat( ...
- SQL 实战(五)
一. 将所有to_date为9999-01-01的全部更新为NULL,且 from_date更新为2001-01-01.CREATE TABLE IF NOT EXISTS titles_test ( ...