1.java是解释型语言。java虚拟机能实现一次编译多次运行。
2.JDK(java software Development kit 软件开发包),JRE(java Runtime Environment java运行环境)。
3.javac编译java程序,java运行java程序。
4.一个文件最多有一个public class。且类定义时只能用public、abstract&final来限定,或者默认状态。
5.switch(condition)中,condition只能是一个整数表达式或者枚举常量,整数表达式可以是int基本类型或Integer包装类型,由于byte,short,char都可以隐含转换为int,所以这些类型以及这些类型的包装类型也是可以的。long不能作用于switch语句中。String在jdk1.7之前的版本也不可以
6.在java中一个字节是八位,一个字符占两个字节(16位unicode字符串)。
7.内存中byte占1个字节,int占4个字节,long类型占8个字节;float占4个字节,double占8个字节;boolean类型占1个字节;
8.java不提供运算符的重载。
9.在static方法中不能访问非static的成员。static方法是指在函数前面加上static限定符,如:public static vooid main(String args[]);public static void print();
10.约定俗成的给package取名时把自己公司的域名倒过来,后面再跟上项目名。如:cn.edu.jxau.Game24。
11.默认的访问权限是default.
12.接口和接口之间可以继承,类和类之间可以继承,但是类和接口之间只能通过类来实现接口。一个类只能继承一个父类,但可以实现多个接口。
13.数组
一维数组的形式:
(1), int a[]; a = new int[5]; 等同于 int a[] = new int[5];
(2), int[] a; a = new int[5]; 等同于 int[] a = new int[5];
二维数组的形式:
int a[][] = {{1,2}, {3,4,5,6}, {7,8,9}};
二维数组可看成以数组为元素的数组。
java中多维数组的声明和初始化应按从高维到低维的顺序进行,如:
方法(1)
int a[][] = new int[3][];
a[0] = new int[2];
a[1] = new int[4];
a[2] = new int[3];//正确
int t[][] = new int[][4];//非法
方法(2);
int a[][] = new int[3][5];//正确,分配一个三行五列的二维数组。
int[][][] b = { //例如: 一个三维数组的静态初始化
{
{1,2,3},
{1,2,3}
},
{
{3,4,1},
{2,3,4}
}
};
第二种方法:称为“匿名数组创建”,它可用于构建和初始化数组,然后将该数组赋予前面已经声明的数组引用变量:
int[] testScores;
testScores = new int[] {4,7,2};
int[][] myArr1 = new int[][]{ new int[]{5,5,2},new int[]{6,9,7,8,1},new int[]{3,2}};
注: 当使用匿名数组创建语法时,不能指定数组的大小。其大小由两个波形符号之间的项数(以逗号分隔)决定。例如下面的这个例子是错误的:
new Object[3] {null, new Object(), new Object() }; //错误
int[][] myArr1 = new int[][]{ new int[3]{5,5,2},new int[5]{6,9,7,8,1},new int[2]{3,2}};// 错误注:int后面添加方括号之后例如 int[] int[][] 就成为了数组类型。而形如 int[3] int[2][3]则不是什么类型 (4) 容易出错的地方: 因为byre、short或char类型能够被显式提升为int并赋予int,所以这些类型的任何数组都能够被赋予int数组。 其实在Java中不能这样做。 int[] dats = new int[4]; char[] letters = new char[5]; dats = letters; //错误 与保存基本类型的数组相反,保存对象引用的数组没有这样的限制。 就像能够将Honda对象放入Car数组一样(因为Honda扩展Car), 也可以将一个Honda类型的数组赋予一个Car数组引用变量。 例如: Car[] cars; Honda[] cuteCars = new Honda[5]; cars = cuteCars; // OK
14.增强的for循环
优点:增强的for循环对于遍历array和collection的时候相当的简便。例如:
import java.util.*;
public class EnhancedFor {
public static void main(String args[]) {
int arr[] = {1,2,3,4,5};
for(int i:arr) {
System.out.println(i);
}
Collection c = new ArrayList();
c.add(new String("sss") );
c.add(new String("aaa") );
c.add(new String("bbb") );
for(Object o:c)
System.out.println(o);
}
}
总结:除了简单遍历并读取其中的内容外,不建议使用增强的for循环。 缺点:数组,不能方便地访问下标值;
15.泛型
JKD1.4以前类型不明确:
装入集合的类型都被当作Object对待,从而失去自己的实际类型;从集合中取出时往往需要转型,效率低,容易产生错误。
解决办法:
在定义集合的时候同时定义集合的类型;
可以在的定义Collection的时候指定;
也可以在循环时用Iterator指定。
好处:
增强程序的可读性和稳定性。
16.线程的基本概念:
(1),线程是一个程序内部的顺序控制流。
(2),线程和进程的区别:
每个进程都有独立的代码和数据空间(进程上下文),进程间切换会有较大的开销;
线程可以看成时轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计时器(pc),线程切换的开销小;
多进程:在操作系统中能同时运行多个任务(程序);
多线程:在同一个应用程序中有多个顺序流同时执行;
17.线程的实现
Java的线程通过java.lang.Thread类来实现。VM启动时会有一个由主方法(public static void main(){} )所定义的线程。可以通过创建Thread的实例来创建新的线程。每一个线程都通过特定的Thread对象的方法run()来完成其操作的,方法run()为线程体。通过调用Thread类的start()方法来启动一个线程。
18.能使用接口的时候不要从Thread类继承,因为用接口不仅可以实现接口的方法,而且还可以继承其他的类。
19.Sleep方法:Thread的静态方法(public static void sleep (long millis) throws InterruptedException) 使得当前线程休眠(暂时停止执行millis毫秒);
Join方法:合并某个线程;
Yield方法:让出CPU,给其他线程执行的机会。
20.synchronized (this) :锁定当前对象,在执行当前这个对象时不应许其他线程打断插入。使用的方式有:
(1),
class Timer {
private static int num = 0;
public synchronized void add(String name) {
//执行这个方法的过程中锁定当前对象。
num ++;
try {
Thread.sleep(1);
} catch( InterruptedException e) {
return;
}
System.out.println(name + ",你是第" + num +"使用Timer的线程。");
}
}
(2),
class Timer {
private static int num = 0;
public void add(String name) {
synchronized(this) { //锁定大括号这个区域的代码段
num ++;
try {
Thread.sleep(1);
} catch( InterruptedException e) {
return;
}
System.out.println(name + ",你是第" + num +"使用Timer的线程。");
}
22.wait方法和sleep方法的区别:21.wait();使用wait()的前提是用synchronized锁住该方法。notify的作用是叫醒其它线程,notifyAll的作用是叫醒其它多个线程。
(1),wait方法是Object类的方法,sleep方法是Thread类的方法。
(2),wait时,别的线程可以访问锁定的对象;调用wait方法时必须锁定该对象。
(3),sleep时,别的线程不可以访问锁定对象。
1. 警惕自增的陷阱
public class Client{
public static void main(String[] args) {
int count =0;
for(int i=0; i<10; i++){
count = count++; ----------(1)
}
System.out.println(“count=”+count);
}
}
这个程序输出的count等于几?是count自加10次吗?答案等于10? 运行结果是0。 为什么呢? 因为count++是一个表达式,是有返回值的,它的返回值就是count自加前的值,Java对后自加是这样处理的:首先把count的值(注意是值,不是引用)拷贝到一个临时变量区,然后对count变量加1,最后返回临时变量区的值。 如果把语句(1)换成count=++count则结果为10。
2. 用偶判断,不用奇判断
String str= i+“----à”+(i%2==1 ? ”奇数”:”偶数”) ;
System.out.println(str);
当i为-1时也将输出 -1----à偶数。 这和Java的取余(%标识符)有关。所以要用偶判断。
3. 用整数类型处理货币
System.out.println(10.00-9.60); 期望的结果是打印0.4。但是结果打印的却是0.4000000000006。 这是因为计算机中浮点数有可能不准确,它只能无限接近准确值,而不能精确。为了解决此问题有两种方法:(1)使用BigDecimal BigDecimal是专门为弥补浮点数无法精确计算的缺憾而设计的类,并且它本身也提供了加减乘除的常用数学算法。特别是与数据库Decimal类型的字段映射时,BigDecimal是最优的解决方案。(2)使用整型 把参与运算的值扩大100倍,并转变为整型,然后再展现时再缩小100倍,这样处理的好处是简单准确,一般在非金融行业应用较多。
4. 给数组扩容
public static <T> T[] expendCapacity( T[] datas,int newLen){
newLen = newLen<0 ? 0:newLen;
return Arrays.copyOf(datas,newLen); //生成一个新数组,并拷贝原值
}
上述代码中采用的是Arrays数组工具类的copyOf方法,产生了一个newLen长度的新数组,并把原有的值拷贝了进去,之后就可以对超长的元素进行赋值了(依据类型的不同分别赋值为0、false或null),使用方法如下:
public static void main(String[] args){
Stu[] classes = new Stu[60];
……..
classes =expandCapacity(classes, 80); //重新初始化超过额度的20人
}
5. 在Java中, RandomAccess和Cloneable、Serializable一样,都是标志性接口。不需要任何实现,只是用来表明其实现类具有某种特质的,实现了Cloneable表明可以被拷贝,实现了Serializable接口表明可以被序列化了,实现了RandomAccess则表明这个类可以随机存取,例如对ArrayList来说也就是标志着其数据元素之间没有关联,即两个位置相邻的元素之间没有相互依赖和索引关系,可以随机访问和存储(即通过下表访问)。
6. 使用shuffle打乱列表
int tagCloudNum = 10;
List<String> tagCloud = newArrayList<String>(tagCloudNum);
Collections.shuffle(tagClouds); //shuffle方法打乱了列表的元素顺序
可以用在抽奖程序等场景中
7. 数组与集合
工具类: 数组的工具类是java.util.Arrays和java.lang.reflect.Array,集合的工具类是java.util.Collections,有了这三个工具类,操作数组和集合会易如反掌,得心应手。
扩展累:集合类当然可以自己扩展了,完全没问题,不过最好的办法还是“拿来主义”,可以使用Apache的commons-collections扩展包,也可以使用Google的google-collections扩展包。
8. 用枚举实现工厂方法模式更简洁
工厂方法模式(Factory MethodPattern)是“创建对象的接口,让子类决定实例化哪一个类,并使一个类的实例化延迟到其子类”。工厂方法模式在开发工作中是被经常用到的模式。下面以汽车制造为例,看看一般的工厂方法模式是如何实现的。
interface Car{
};
class FordCar implements Car {
};
class BuickCar implements Car{
};
//工厂类
class CarFactory {
public static Car createCar(Class<? extends Car> c) {
try {
return (Car)c.newInstance();
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
这是最原始的工厂方法模式,有两个产品:福特汽车和别克汽车,然后通过工厂方法模式来生产。有了工厂方法模式,就不用关心一辆车具体是怎么生成的了,只要告诉工厂“给我生产一辆福特汽车”就可以了,下面是产出一辆福特汽车时客户端的代码:
public static void main(String[] args) {
//生产车辆
Car car =CarFactory.createCar(FordCar.class);
}
这就是我么经常使用到的工厂方法模式,但经常使用并不代表就是最优秀、最简洁的。此处再介绍一种通过枚举实现工厂方法模式的方案。枚举实现工厂方法模式有两种方法:
(1) 枚举非静态方法实现工厂方法模式
我们知道每个枚举项都是该枚举的实例对象,那是不是定义一个方法可以生产每个枚举项对应产品来实现此模式。代码如下:
eum CarFactory {
FordCar, BuickCar;
Public Car create() {
switch (this){
case FordCar:
return new FordCar();
case BuickCar:
return new BuickCar();
default :
throw new AssertionError(“无效参数”);
}
}
}
Create 是一个非静态方法,也就是只有通过FordCar、BuickCar枚举项才能访问。采用这种方式实现工厂方法模式时,客户端要生产一辆汽车就很简单,如下:
public static void main(String[] args) {
Car car = CarFactory.BuickCar.create();
}
(2) 通过抽象方法生成产品
类型虽然不能继承,但是可以用abstract修饰其方法,此时就表示该枚举是一个抽象枚举,需要每个枚举项自行实现该方法,也就是说枚举项的类型是该枚举一个子类,代码如下:
enum CarFactory {
FordCar {
public Car create(){
returnnew FordCar();
}
BuickCar{
public Car create(){
return newBuickCar();
};
public abstract Car create() ;
}
首先定义一个抽象制造方法create,然后每个枚举项自行实现。这种方法编译后会产生两个CarFactory的匿名子类,因为每个枚举项都要实现抽象create方法。客户端调用与上一个方案是一样的。
使用枚举类型的工厂方法模式的优点有:I 避免错误调用的发生 II 性能好,使用便捷 III降低类间耦合。
9. 一旦创建了字符串,它的内容就不能改变。 方法toLowerCase、toUpperCase、trim、replace、replaceFirst和replaceAll会返回一个源自原始字符串的新字符串(未改变原始字符串!)。在java中,字符串不是数组,但是字符串可以转换成数组,反之亦然。
10. 统计字符串中的每个字母./**
/**
* @param s
* @see 统计字符串中的每个字母
*/
private static int[] countLetters(String s) {
// TODO Auto-generatedmethod stub
int[] counts =new int[26];
for(int i=0; i<s.length(); i++){
if(Character.isLetter(s.charAt(i)))//指定字符是一个字母
counts[s.charAt(i)-'a']++;
}
return counts;
}
显示字符出现次数的时候用 counts[i]表示字母(char)(‘a’+i)出现的次数。
11. Character类和StringBuilder/StringBuffer类中有很多有用很方便的方法。Character.isLetter(ch), Character.isLetterOrDigit(ch), Character.isUpperCase 判断指定字符是否是字母、字母或数字、是否是大写字母等
StringBuilder中有 reverse( ); 反转生成器中的字符等方法
String类有replace(),replaceAll(),getChar(), split()等方法
12.File类
文件名是一个字符串。File类是文件名及其目录路径的一个包装类。 例如,在Windows中,语句 newFile(“c:\\book”)在目录c:\book下创建一个File对象,而语句new File(“c:\book\test.dat”);为文件c:\book\test.dat创建一个File对象。可以用File类的isDirectory()方法来判断这个对象是否表示一个目录,还可以用isFile()方法来判断这个对象是否表示一个文件名。
注意:构建一个File实例并不会在机器上创建一个额问你件。不管文件对象是否存在,都可以创建任意文件名的File实例。可以调用File对象上的exists()方法来判断这个文件是否存在。
boolean |
mkdir() 创建此抽象路径名指定的目录。 |
boolean |
mkdirs() 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。 |
String |
toString() 返回此抽象路径名的路径名字符串。 |
URI |
toURI() 构造一个表示此抽象路径名的 file: URI。 |
URL |
toURL() 已过时。 此方法不会自动转义 URL 中的非法字符。建议新的代码使用以下方式将抽象路径名转换为 URL:首先通过 toURI 方法将其转换为 URI,然后通过 URI.toURL 方法将 URI 装换为 URL。 |
String[] |
list() 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。 |
String[] |
list(Filename Filter filter)返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。 |
File[] |
listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。 |
File[] |
listFiles(FileFilter filter)返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 |
boolean |
createNewFile() 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。 |
13. 文件输入输出
File对象封装了文件或路径的属性,但是它既不包括创建文件,也不包括从(向)文件读(写)数据的方法。为了完成I/O操作,需要使用恰当的Java I/O类创建对象。这些对象包含从(向)文件读(写)数据的方法。
可以使用PrintWriter写数据。例如PrintWriter output = new PrintWriter(new File(“scores.txt”)); 然后调用PrintWriter对象上的print、println和printfy方法向文件写入数据。 需要注意的是最后必须使用close()方法关闭文件,如果没有调用该方法,数据就不能正确的保存在文件中。
使用Scanner读数据。 为了能从键盘读取数据,需要为System.in创建一个Scanner对象,例如 Scannerinput =new Scanner(System.in); 为了从文件中读取数据,需要为文件创建一个Scanner对象,例如Scanner input =new Scanner(new File(filename));
Scanner如何工作:
方法nextByte(),nexShort(),nextInt(),nextFloat()和next()等都被称为令牌读取方法,因为它们会读取用分隔符分隔开的令牌。默认情况下,分隔符是空格。可以使用useDelimiter(String regex)方法设置新的分隔符模式。
一个输入方法是如何工作的呢?一个令牌读取方法首先跳过任意分隔符(默认情况下为空格),然后读取一个以分隔符结束的令牌。然后,对应于nextByte(), nextShort(),nextInt(),nextFloat(),这个令牌就分别自动地转换为一个Byte, Short,Int,Float型的值。对于next()方法而言是无须做转换的。如果令牌和期望的类型不匹配,就会抛出一个运行异常java.util.InputMismatchException。
方法next()和nextLine()都会读取一个字符串。next()方法读取一个由分隔符分隔的字符串,但是nextLine()读取一个以行分隔符结束的行。
14.二进制I/O
Java提供了许多实现文件输入/输出的类。 这些类可以分为文本I/O类(text I/Oclass)和二进制I/O类(binary I/O calss)。上面提到的PrintWriter和Scanner就是文本I/O类。
文本I/O和二进制I/O
计算机并不区分二进制文件与文本文件。所有的文件都是以二进制形式来存储的,因此从本质上来说,所有的文件都是二进制文件。文本I/O建立在二进制I/O的基础之上,它能提供字符层次的编码和解码的抽象。在文本I/O中自动进行编码和解码。在写入一个字符时,Java虚拟机会将统一码转化为文件制定的编码,而在读取字符时,将文件指定的编码转化为统一码。 二进制I/O不需要转化。如果使用二进制I/O向文件写入一个数值,就是将内存中的确切值复制到文件中。 所以它比文本I/O效率高,且与主机的编码方案无关,是可移植的。 一般来说,对于文本编辑器或文本输出程序创建的文件,应该使用文本I/O来读取,对于Java二进制输出程序创建的文件,应该使用二进制I/O来读取。但并不是原则问题,一定要这样做。
15. Java变量分为局部变量和成员变量,成员变量又根据有没static修饰分为类变量(静态变量)和实例变量(非静态变量)。局部变量必须进行显示的初始化,而成员变量则不是必须的。 所有局部变量都是放在栈内存里保存的,不管其是基本类型的变量,还是引用类型的变量,都是存储在各自的方法栈内存中的;但引用类型的变量所引用的对象(包括数组、普通的Java对象)则总是存储在堆内存中。
Java类库结构
Java类库包含在java开发工具JDK中,JDK是Sun公司在JavaSoftware产品。Java类库包含接口和类,每个包中又有许多特定功能的接口和类,用户可以从包开始访问包中的接口、类、变量和方法。下面粗略介绍一下各个包的功能。
1. java.lang 包
他是java语言核心包,包括java语言基础类,如基本数据类型、基本数值函数、字符串处理、线程、异常处理等。其中的类Object是最重要的,它是java中所有类的基础类,不需要用import语句引入。也就是说,每个程序运行时,系统都会自动引入java.lang包
2. java.io 包
它包含了用于数据输入输出的类,主要用于支持与设备有关的数据输入输出,即数据流输入输出、文件输入输出、缓冲区流以及其他设备的流入流出。凡是需要完成与操作系统相关的教底层的操作,都应该在程序的首部引入java.io 包
3. java.applet 包
它提供了创建用于浏览器的Applet小程序所需要的类,包括以下几个类:AppletContext、AppletStub、AudioClip、Applet。
开发Applet小程序时,必须引入java.applet包,并把应用程序的主类声明为Applet的子类
4. java.awt 包
AWT(Abstract Window Toolkit)抽象窗口工具集,它提供了图形用户界面设计、窗口操作、布局管理和用户交互控制、事件响应的类,如Graphic图形类、Diolog对话框类、Button类、、、其中的Component组件类是java.awt 包中所有类的基类。
5. java.net 包
java网络包,提供了网络应用的支持,包括三种类型的类:用于访问Internet资源及调用CGI通用网关接口的类,如URL;用于实现套接字接口Socket网络应用的类,如ServerSocket和socket;支持数据报网络应用的类,如DatatgramPacket等
6. java.math 包
java语言数学包,包括数学运算类和小数运算类,提供完善的数学运算方法,如数值运算方法、求最值、数值比较和类型转换
7. java.util 包
java使用程序包,提供了许多实用工具,如日期时间类(Date),堆栈类(Stack),哈希表类(Hash),向量类(Vector),随机数类,系统属性类等。
8. java.security 包
java安全包,提供了网络安全架构所需的类和接口,可以有效地管理和控制程序的安全性,如给程序进行数字签名、加密等
9. java.sql 包
java数据库包,提供了java语言访问处理数据库的接口和类,它是实现JDBC的核心类库。安装相应的数据库驱动程序名,java程序就可以访问诸如SQL Server、Oracle、DB2等数据库,进一步提供了java程序的平台无关性。
10. java.rmi 包
java远程方法调用包,java.rmi 包提供了实现远程方法调用(Remote Method Invocation)所需的类,用户远程方法调用是指用户程序基于JVM在远程计算机上创建对象,并在本地计算机上使用这个对象。
11. java.text 包
java文本包,提供了一种独立于自然语言的方式处理文本、日期、数字、消息的类和接口,实现日期、数字、消息的格式化、字符串搜索排序的功能。
12. java.beans 包
提供了一系列与JavaBean开发有关的类