[读书笔记]java核心技术
ps:有时间好好整理下格式。从别的编辑器拷贝过来啥都没了。
~~~~~~~~~~~~~~·
2、java程序设计环境
JDK 开发java使用的软件;
JRE 运行java使用的软件;
SE 用于桌面或简单服务器应用的java平台--废弃
EE 用于复杂服务器应用的java平台--通用。
ME 手机或其他小型设备的java平台--废弃
库源文件和文档: src.zip---包含了所有公共类库的源代码。
JDK目录结构:
bin 编译器和工具, demo演示, docs 类库文档,include 用于编译本地方法的文件,jre运行环境文件,lib 类库文件,src类库源文件。
命令行
javac程序是java的编译器,把java后缀的文件编译成.class文件,并发送到java虚拟机,虚拟机执行编译器放在class文件中的字节码。
注意大小写,编译时需要文件名,运行时需要类名。
appletviewer程序 ,jdk自带的快速测试applet,需要提供一个HTML文件名。可在浏览器中启用applet查看。
3、java的基本程序设计结构
System.out.println("xxxx"); //out.print输出不换行。
大小写敏感,类名已大写字母开头,文件名必须与公共类的名字相同,用.java扩展。强调main也public。
/** 这种注释可以自动生成
*文档*/ //换行时加个* 明显标识
强类型语言,8种基本类型(4整型、2浮点、1Unicode编码的字符单元char,1boolean);
1整型 没有小数部分的数值,可负数, int 4字节 ,short 2字节,long 8字节 , byte 1字节;
与运行java代码的机器无关。长整型数值加后缀L,数字加下划线只为了让人更易读,编译器会去除。
2浮点类型 float4字节,double8字节 。float类型数值有后缀F(3.14F),没有默认是double(或加D)。
Double.NaN 不是数字?
if(x == Double.NaN) //永远false,
if(Double.isNaN(x)) //检查x
浮点型不适用于禁止出现舍入误差的金融计算。 System.out.println(2.0-1.1)将打印0.89999999。
使用BigDecimal类实现这类运算。
3char类型 单字符,
转义字符 \b退格,\t制表, \r换行=\u000a, \n回车=\u000d,
Unicode 范围从\u0000-\uffff。
JDK5.0开始, 代码点(code point)与一个编码表中的某个字符对应的代码值;看不懂喽~P52~
4boolean 类型,false和true,和整型值不能进行相互转换。
用final指示常量,
final double PI=3.14; //名称全大写
用strictfp关键字指定方法必须使用严格的浮点计算来产生理想的结果。
位运算>>>用0填充高位,>>用符号位填充高位。
Math类注重性能,如果注重结果使用StrictMath类;
对浮点数进行舍入运算,得到最接近的整数 Math.round(x);--round默认返回long类型。
枚举: enum Size{ SMALL,MEDIUM,LARGE,EXTRA_LARGE}; Size s=Size.SMALL;
字符串
s.substring(0,3);
String 类对象称为不可变字符串,
s.equals(t) //是否相等。 不区分大小写用 equalsIgnoreCase;
空串是"",长度0的字符串, if(str.length()==0) //检查空串
if(str!=null) //检查是否null;
char charAt(int index)
boolean endsWith(String suffix);
toLowerCase/toUpperCase, 小写/大写
构建字符串 StringBuilder类比拼接(+)高效;
StringBuilder sb=new StringBuilder(); sb.append(chOrStr); sb.toString();
StringBuffer效率低点,但允许多线程执行 添加或删除字符;
所在包 java.lang.StringBuilder
void setCharAt(int i, char c);将i位置设置为c;
StringBuilder insert(int offset, StrOrChar s);
StringBuilder delete(int startIndex, int endIndex);
输入输出
System.out.println ,输出
标准输入流 需要构造一个Scanner对象 // 所在包 import java.util.*;
Scanner in=new Scanner(System.in);
String name=in.nextLine();//读取一行
String aWord=in.next();//读取直到遇到空格;
int age=in.nextInt();
boolean hasNext(); hasNextInt();
Scanner类是输入可见的,不适合密码;
Console cons=System.console(); char[] passwd = cons.readPassword("Password:");
包:java.io.Console
System.out.printf("%,.2f",1000.0/3.0); //打印 3,333.33
可使用多个标志, +正负号,-左对齐,.分组分隔符,(负数加括号;
可使用String.format方法创建一个格式化的字符串但不打印;
时间日期 System.out.printf("%tc", new Date());
c 完整日期和时间, F :2004-02-09 ,D 02/09/2004, T 24小时时间18:05:19,
文件输入输出
读入文件Scanner in=new Scanner(Paths.get("myfile.txt")); //java.util.Scanner
写入文件PrintWriter out=new PrintWriter("myfile.txt");//如果文件不存在则创建;java.io包
out.println("you can !");
写入后需调用out.flush(); 刷新流;
用户路径 String dir=System.getProperty("user.dir"); //java.nio.file.Paths
如果用不存在的文件构造一个Scanner,或不能被创建的文件名构造一个PrintWriter,会异常;
在方法中用throws子句标记, void sub() throws FileNotFoundException {};
流程控制
while(condition)statement;
do statement while(condition);
for(int i=1; i<=10; i++){}
switch(x){ case 1: break; ...}
-不建议使用switch,可在编译时加 -Xlint:fallthrough选项,强制少break警告;
-也可在外围方法加标注@SuppressWarnings("fallthrough");消除警告;
带标签的break和continue:标签是 name:, break/continue到标签后的语句块末尾,再检查是否正常退出语句块; 都不建议使用;
大数值 java.math 中的 BigInteger和 BigDecimal。可任意长度;
BigInteger a = BigInteger.valueOf(100); a.add(b);//不能用 + - * /
int compareTo(BitInteger other);//0相等,正数是大于,负数是小于;
数组
int[] a=new int[100];
System.out.println(Arrays.toString(a));//输出"[a1,a2.....]"
数组不能改变大小, 数组列表(array list)才可以。
for(variable: collection)statement; //实现了Iterable接口的类对象也可以。
初始化 int[] a={2,3,4}; //不需要new
int[] a=new int[]{2,3,4};
数组拷贝 int[] a= Arrays.copyOf(b, x * b.length); //通常用来增加数组的大小;
排序 Arrays.sort(a);//优化的快速排序算法;
Math.random() 返回0到1(包括0,但不包括1)的随机浮点数;
抽取算法:用数组最后的元素值改写抽取值,然后数组可用长度减一;
numbers[r] = numbers[n - 1]; n--;
static int binarySearch(type[] a, type v);//二分搜索算法查找值v
static void fill(type[] a, type v); //数组所有填充值v;
多维数组 int[][] x;
System.out.println(Arrays.deepToString(x));//输出"[[3,2...],[a1,a2.....]...]"
4 对象与类
三主要特性: 行为、状态、标识;
类之间的关系 依赖(uses-a)、聚合(has-a)、继承(is-a);
对象与对象变量的区别; 一个对象变量并没有实际包含一个对象,仅仅引用;
日历类 GregorianCalender类,
表示时间点的Date类:时间是用距离一个固定时间点(纪元 1970-01-01 00:00:00)的毫秒数标识的,
GregorianCalender thisTime=new GregorianCalender(); //月份从0开始计数。
int month = thisTime.get(Calendar.MONTH); //通过常量和访问器来获取年月日等,
thisTime.set(属性,value);thisTime.add(属性,vlaue);
Date time=thisTime.getTime();//setTime(time); 转换。
int firstDayOfWeek = d.getFirstDayOfWeek();//一个星期中的第一天;
java.text.DateFormatSymbols 下面有几个格式输出方法
String[] getShortWeekdays()/ getShortMonths();
自定义类 在一个源文件中,只能有一个公有类;
警告: 不要编写返回引用可变对象的访问其方法;这个引用可能在外面被改写,破坏了类的封装。
代替用 对象的clone();
静态域与静态方法
static修饰的变量,所有实例共享一个静态域; 可用于标识实例;
private static int nextId=1; private int id;
void setId(){ id=nextId; nextId++;}
System有setOut方法,是本地方法,可设置不同的流,不是用java实现的。不推荐。
可使用对象名调用静态方法但不推荐;
工厂方法: 静态方法的常见用途,构造不同对象;
每一个类可以有个main,用作单元测试。
方法参数:java总是按值传递,没有引用调用!对象引用进行的也是值拷贝传递;
对象构造
重载,多个构造器(通过不同的方法签名-方法名和参数 实现);
默认域初始化,没有显式的构造器。不推荐,影响代码可读性;
无参构造器,常用;
参数名
public Employee(String aName, int aAge){name=aName;}//用a前缀;
public Employee(String name, int age){this.name=name;}//用this访问当前对象
用this在构造器中调用另一个构造器,重用代码;
public Employee(int age){ this("NewName",age);}
初始化块
一个类的声明中,可以包含多个代码块, {xxxx; xxx; xxxxxxx;}
三种初始化数据域的方法:构造器中赋值,声明时赋值,初始化块中赋值。
初始化块先于构造器执行;
static 修饰的块只执行一次,其他的每次类实例化都执行;
调用构造器的具体步骤
a、所有数据域被初始化为默认值(0/false/null);
b、按声明中出现的次序,依次执行初始化语句和初始化块;
c、如构造器1调用了构造器2,则先执行2,再执行1。
对象析构与 finalize方法
Java不支持析构器;但当使用了文件或另一系统资源的句柄,使用finalize方法,它在垃圾回收器清除对象之前调用。实际中,不要依赖于该方法回收任何短缺的资源——不知道何时才回收。
使用关闭勾:Runtime.addShutdownHook,可确保在Java关闭前调用finalize方法;
包package
确保类名的唯一性;嵌套的包之间没有任何关系!
调用其他包的类,可在类名之前加包名,或 用import语句;
静态导入 用import可导入静态方法和静态域;
import static java.lang.System.*; //可使用System的静态方法和静态域;
out.println("xxxx"); exit(0);
包作用域 不显示声明private的类、方法、变量,可被同一包的所有方法访问;破坏封装。
类路径 类路径必须与包名匹配;
JAR(Java归档)文件,可以用压缩文件工具打开查看JAVA文件;
共享类需要: P156的4.8类路径,暂时不懂。
文档注释
工具javadoc,由源文件生成一个HTML文档;
以专用的定界符/**开始的注释, 和代码同步;
javadoc的程序utility抽取信息(包、公有类和接口等),信息是自由格式文本,标记有@开始, 文本第一句是概要性句子;
方法注释 @param 标量描述, @return描述,@throws类描述,表示有可能抛出异常。
@author 姓名 --类注释,作者
@version 文本 --版本条目
@since 文本 始于条目,引入特性的版本
@see 引用 超级链接,可选三种
@see package.class#feature label
@see <a href="www.baidu.com">label</a>
@see "text"
包注释 需在每一个包目录中添加一个单独的文件,
以package.html命名的文件,或package-info.java命名的java文件。P162.
注释抽取 javadoc -d docDirectory nameOfPackage
类设计技巧
保证数据私有(不破坏封装);对数据初始化;不在类中使用过多基本类型;缩减set或get访问器;分解过多的职责到其他类;类名和方法名提现职责;
java不对局部变量初始化,但对对象的实例域初始化。
比如用Address类代替 String street/city/state等。
5 继承
5.1类、超类、子类
继承是 is-A 关系,关键字extends ,(父类、超类、基类),子类;
置换法则:出现基类对象的地方都可以用子类置换。 //能置换,所以方法不能降低可见性;
调用基类方法:super.xxx(); //this是对象引用,super只指示编译器调用超类方法。
类似super();//调用超类对应构造器;放在自身构造器的第一条语句;
覆盖 override,
多态:一个对象变量可指示多种实际类型的现象,运行时自动选择调用哪个方法,称为动态绑定。
--申明为基类的引用,实际可引用基类实例,也可引用子类实例,虚拟机根据实际的引用来调用相关(公有的,非子类特有新增的)方法。 申明为基类的引用,肯定不能调用子类的方法。
subA[] aList=new subA[3]; baseA[] sList=aList; //这时aList和sList将指向同一对象;
aList.subDo(); //ok
sList.subDo();//ERROR!虽然指向同一个对象。
方法是private /static/ final 的或构造器,调用它们是静态绑定。
虚拟机为每个类创建了一个方法表,调用时直接查表;
阻止继承: final类和方法
类的特定方法可被声明为final,这样子类不能覆盖它;final类的所有方法自动的称为final方法。
有些认为:不使用多态的都声明为final。
早期java中,避免动态绑定带来系统开销而使用final关键字。
一个方法没有被覆盖且很短,编译器优化(内联 inlining),e.getName()被转换为e.name域,提升效率;
强制类型转换
当一个基类声明中引用的确实是子类的对象实例时,可以强制类型转换为子类,或者说,用一个子类的声明去指向这个基类所引用的对象实例;
BaseA aBaseA=new SubA(); SubA a = (SubA)aBaseA;
//暂时忽视申明类型,使用对象的全部功能。
可用 instanceof 关键字 测试 if(a instanceof SubA){dosomething;}
抽象类 : abstract修饰,且包含一个或多个抽象方法的类本身必须被声明为抽象。
抽象类还可以包含具体数据和具体方法。
抽象类不能被实例化,但可以有构造器,在子类的构造器中 用super()调用它;
使用通配符 编译多文件 :javac *.java; 或javac -d . A.java B.java ,生成到下一级目录。
受保护访问:protected,仅子类可访问;--修改可能影响子类,违背OOP的封装原则;--对同包其他类都可见。
控制可见性
1、仅本类-private; 2 对所有类-public; 3、对本包和所有子类可见-protected; 4、对本包--默认,不需修饰符。
5.2 Object:所有类的超类
Object obj=new Employee("name",age);//Object类型的变量可引用任何类型的对象。
只有基本类型不是对象;数组类型(哪怕基本类型的数组)都扩展于Object类。
equals 对于对象是判断俩对象是否具有相同的引用。
通常对类来说是无意义的,所以每个类要实现自己的equals。
编写一个完美equals的建议:
0、参数命名otherObject, 显式转换后为other;类型是Object,这样才能完全覆盖超类。
1、判断引用是否相同 this==otherObject; return true;
2、判断是否null otherObject==null return false;
3、判断类型一致 getClass()!=otherObject.getClass() //false
4、将otherObject强制转换为当前类的对象,判断状态。
Employee other= (Employee)otherObject;
Objects.equals(other.name,this.name);
static Objects.equals(a,b);若a,b都为null,则true,若一个null,返回false;
static Objects.equals(type[] a,type[] b); 数组长度相同,且对应位置元素也相同,则true;
子类中定义equals方法时,要先调用超类的equals。
相等测试与继承 instanceof 判断可能是继承关系,所以不适合这种判断。
如果equals的语义在子类中有所改变,则用getClass,
如果没改变,用instanceof,且将超类的eauals声明为final 。
equals 对任何非空引用,满足 自反性,对称性,传递性,一致性。对于空引用,返回false;
hashCode方法
散列码 是由对象导出的一个整型值。Object类的对象默认为其存储地址。
如果重定义equals,必须重新定义hashCode,以便用户将对象插入得到散列表中。
一般: Objects.hash(p1,p2,p3);//组合类的多个属性,生成散列码。
(java.lang.Object 1.0包) int hashCode() 返回散列码,相等的对象要求返回相等的散列码。
int Objects.hash(o1,o2,o3....) 由提供的所有对象的散列码组合得到新的散列码
int Objects.hashCode(Object a) 如果a为null则返回0,否则返回a.hashCode()。
(java.util.Arrays 包) static int hashCode(type[] a) 数组的散列码
toString方法
对象的toString可自定义。建议 类名加方括号的域值,如 java.awt.Point[x=20,y=30] ;
类名用 a.getClass().getName();获取;
技巧:在需要调用x.toString()的地方,用""+x替代。 这样x是基本类型时也能够执行。
只要对象与字符串通过"+"连接 ,Java编译就自动调用toString方法。
也可以用System.out.println(x);//是对象时默认x.toString();
Object的toString,是输出对象的所属类名和散列码。
数组用Arrays.toString(a); 多维用Arrays.deepToString(b);
调试信息: System.out.println("Current ObjectX =" + obj); //需要对象实现自己的toString();
强烈建议为自定义的每一个类增加toString()方法;
java.lang.Object包 Class getClass() 返回包含对象信息的类对象;
java.lang.Class 包 Class getSuperclass()返回类的超类信息;
5.3泛型数组列表
Java允许在运行时确定数组大小,int size=100; Employee[] staff=new Employee[size];
泛型里面的类型参数必须是 Object 的子类。
ArrayList是采用 类型参数的泛型类, <Employee>指定类型,
ArrayList<Employee> staff=new ArrayList<>();
//Vector类是老版本的动态数组, 老版本中的ArrayList类元素是Object,没有泛型。
数组列表管理着内部数组的引用,若数组已满,则自动创建更大的数组并copy元素到新数组中。
如已清楚或能估算穿数组元素数量,可调用ensureCapacity方法,分配一个包含指定数量元素的内部数组。
也可以 ArrayList<Employee> staff=new ArrayList<>(100); //指定数量
.size()返回实际元素数目。
.add(x)添加元素, .add(index,x); .remove(n);
.trimToSize()确认元素不会再增删后,将数组列表的存储容量消减到当前尺寸,剩下的空间以便垃圾回收器回收。
new ArrayList<T>(); 构造空数组列表;
new ArrayList<T>(initialCapacity);用指定容量构造一个空数组列表。
staff.set(i,harry); //set和get 访问元素,注意实际的size;
5.4对象包装器与自动装箱
需要将基本类型转换为对象时用到。int-Integer,包装器(wrapper)是final的。
对象包装器是不可变的!不允许更改便装在其中的值。
如:ArrayList<int> aInt=new ArrayList<int>();
报错: Syntax error, insert "Dimensions" to complete ReferenceType。
泛型数组列表里面的类型参数必须是 Object 的子类,因此不能使用 int,而应该使用 int 的包装器类型 Integer。
ArrayList<Integer> aInt=new ArrayList<Integer>();
list.add(3) 自动转换为 list.add(Integer.valueOf(3)); ---自动装箱(autoboxing)。
int n=list.get(i); 自动转为list.get(i).intValue():自动拆箱;
类似:Integer n=3; n++; ---- 先拆箱,再自增,再装箱。
包装器对象比较,调用equals();
装箱和拆箱是编译器的工作,是语法糖。虚拟机执行的是编译器编译的字节码。
包装器还可以包含一些数值/字符串 转换方法。
int x=Integer.parseInt(s);
System.out.println(Integer.toString(System.out.hashCode(),16));
toString(int i, int radix) //返回数值i的给定参数进制的表示;
intValue() 返回包装器的值。
int x=Integer.parseInt(s,int radix);//radix是说明字符串s的进制表现形式。
5.5参数数量可变的方法
Java SE5.0以后提供的,
如printf,一个格式化字符串,一个Object[]
public static double max(double...values){
for(double v : values) if(x)doSomething();} //...是无限参数
5.6枚举类
public enum Size{SMALL,MEDIUM,LARGE,EXTRA_LARGE};
是有4个实例的类; Enum类的子类是Size。
比较俩枚举值时不需要调用equals,直接==;
x.toString()将返回字符串;
Enum Enum.valueOf(Size.class,"SMALL")是逆方法,
Size.values()返回包含全部枚举值的数组;
ordinal()返回枚举的位置,从0开始基数。
int compareTo(E other) 在前为负值,this==other则0,在后则正值。
5.7反射
反射库(reflection library),JavaBeans中大量使用。
能够分析类能力的程序称为反射(reflective)。
运行中分析类的能力;运行中查看对象;---使用它的主要是工具构造者。
1、Class类)
程序运行期间,Java运行时系统为所有对象维护一个被称为运行时的类型标识。
记录着对象所属的类。
Object类的getClass()返回一个Class类型的实例;
Class.forName(className);返回类名对应的Class对象;
也可以用T.class来获取类对象;
一个Class对象实际上表示一个类型,但未必一定是类,如int.class。
可以用==比较类对象;
x.getClass().newInstance();快速创建一个类的实例;(需要一个无参的构造器)
forName和newInstance配合使用,可由字符串中的类名创建一个对象。
String s="java.util.Date";
Object m=Class.forName(s).newInstance();
2、捕获异常)
异常类型: 未检查异常和已检查异常。
try{ dosomethings();}catch(Exception e){}
Throwable类(Exception的超类)的printStackTrace()打印出栈的轨迹;
3利用反射分析类的能力)--暂忽略
java.lang.reflect包中Field/Method/Constructior分别描述类的域、方法、构造器。
还有Modifier类的静态方法;
4运行时使用反射,5使用反射编写泛型数组代码、6调用任意方法)暂忽略
5.8继承设计的技巧
1公共操作和域放在超类;
2不使用受保护的域;
(子类无限制,谁都可以写个派生类并访问protected实例域,破坏封装)
(java中,同一个包中的所有类都可以访问proteced域。)
3使用继承实现 is-a关系;(钟点工不是特殊的雇员,不是is-a关系,不能继承)
4除非所有继承的方法都有意义,否则也不要使用继承。
5覆盖方法不产生非预期行为;-覆盖基类的方法时,不偏离最初的设计想法。
6使用多态,而非类型信息;
7不过多使用反射;
6 接口和内部类
接口interface-对象克隆-内部类inner class-代理proxy
6.1接口 对类的一组需求描述,类要遵从接口描述。
接口中所有方法自动属于public。所有域是public static final。
更重要的是知道接口不能提供哪些功能。
接口可以定义常量,但不能含有实例域,也不能有方法实现。
implements关键字。
java.lang.Comparable<T>的int compareTo(T other); 小于则负值,相等0,大于正值。
强制的是 “实现接口”,而不是实现方法。----------一定注意。
java.util.Arrays包的 static void sort(Object[] a)
对数组a的元素排序,要求元素必须属于实现了Comparable接口的类。
如果父子类对象比较的含义不一样,那属于不同对象的非法比较。或者比较定义在超类且final。
1接口特性)不是类,不能new实例化。但能声明变量,变量引用实现了接口的类对象。
使用instance检查一个对象是否实现了某个特定接口
if(anObject instanceof XInterface){}
接口可以继承接口 interface X extnds xBase;
接口可以只定义常量,但有点偏离初衷,建议NO。
6.2对象克隆
Object类的clone方法默认是浅拷贝。
对于每一个类,进行如下判断:
1默认的clone是否满足,2默认的clone是否能通过调用子对象的clone得到修补,3是否不应该使用clone。
对于不满足1和2的, 实现Cloneable接口,使用public重新定义clone。
Cloneable接口的出现和接口的正常使用没有任何关系。clone方法是从Object类集成而来。
接口只作为标记,表明类设计者知道要进行克隆处理。
------标记接口tagging interface或者marker interface。用途是可以使用instanceof进行检查。
即便默认实现(浅拷贝)满足,也该实现Cloneable接口,且定义public并调用super.clone();
public X clone throws CloneNotSupportedException
{ return (x)super.clone();}
所有数组类型均包含clone方法,且为public,可以创建一个新数组。
int[] cloned= luckyNumbers.clone();
6.3接口与回调 callback
回调,指出某特定事件发生时采取的动作。
Java标准类库传递一个对象给定时器,定时器调用其方法;要求对象所属类实现了java.awt.event包的ActionListener接口。里面一个方法void actionPerformed(ActionEvent event);
定时器调用actionPerformed方法。
一个例子-明天运行一下。且整理笔记。
6.4内部类
已读,笔记待整理。
结语:我只想把我所知道的,尽量简洁清楚地表达出来。