1、关于递归,一定要注意函数调用顺序!
图1
如上图:在执行f(n-1)+f(n-2)的过程中,先执行f(n-1)一直到f(n-1)有返回值才执行f(n-2)。
2、
图2
注意成员变量和局部变量的区别:关于成员变量见上图,但是在使用局部变量之前必须对之初始化!否则编译会报错(如果只是声明一个局部变量,而在以后的语句中不使用它的话就不会报错)!!
3、
类是静态(代码)的概念,位于代码区;
对象是new出来的,位于堆内存(可以动态分配),类的每个成员变量在不同的对象中都有不同的值(除了静态变量)而方法只有一份,方法执行的时候才占用内存。
同一类的每个对象有不同的成员变量存储空间。
同一类的每个对象共享该类的方法。
4、
局部变量(包括基本变量和引用变量等)、形参等在stack(栈内存)中声明!!!
5、构造方法没有返回值!!!构造方法和类名一样,开头大写,其他方法都是小写。
6、类的书写习惯为:
图3
“}”单独占一行,属性和方法、方法和方法之间隔一行。
7、
方法执行后,之前分配的空间(在栈中,因为方法中变量---局部变量,所占的空间都是栈空间)就立马消逝了。
8、
类的方法只是一段代码(所有的对象共享),只有在调用的时候才会在内存各个部分分配空间。
9、
非静态方法是针对每个对象进行调用
10、
堆内存中,成员变量也可以指向堆内存中的其他堆。如下图:o是引用类型
图4
11、
函数的返回值存在于栈内存的临时空间里。
12、
this可以看做是一个变量,它的值是当前对象的引用。如下图:在堆内存中
图5
静态方法不再是针对某个对象调用,所以不能访问非静态成员(非静态成员和非静态方法只能针对某个对象调用!!!)
图8
注意:static声明的静态成员变量,放在data seg(数据区)里,注意,字符串常量也放在data seg里。
14、
把上述代码的static去掉的代码如下:
图9
15、
关于package import的总结:
class文件必须在正确的包目录下:例如在Cat类中,package com.a.b;则Cat.class必须位于com\a\b下!
必须class文件的最上层的父目录位于classpath下;
执行一个类需要写全包名:java com.sh.gczl.Cat;(配置环境变量或者进入到父目录)
关于打包:进入classpath下,jar -cvf test.jar *.* //*表示所有的文件
16、
关于继承:new一个子类实例之前先new父类。
17、
重写:
方法名称、返回值类型、参数全部一样;但是权限可以不一样~!!
重写方法不能使用比被重写方法更严格的访问权限。
18、
关于super:
图12
this,super都是引用。在new子类的时候先new一个父类,所以父类实例在子类实例里面。
图13
19、
当成员变量的值是字符串时,则变量的引用指向“data seg”里的字符串。
20、
java编译器按照classpath(“.;其他路径”--注意两者的顺序(谁在前就先按谁的路径下找类)!!)的顺序来找类进行编译。
21、
Object的equals方法的默认实现是“x==y”;即比较引用。
equals是单三形式,因为是对象和对象之间的比较。
图14
22、
Dog d=new Dog();
System.out.println(d);
默认会调用d.toString()方法,返回“类名@哈希码”。
哈希码是唯一表示对象的。
23、
图15
注意第二句话!!
24、
图16、17
26、
多态的三个条件:要有继承、要有重写、父类引用指向子类对象
图21、22
图19、20
如果extends了抽象类后不想实现其中的抽象方法的话,可以把自己也声明成抽象类,自己的方法也是抽象方法,然后把实现交给自己的子类就行了。
28、
图24
例(1)
public class Test{
public static void main(String[]a){
A a=new A();
a.i=9;
}
}
class A{
final int i=9;
}
编译报错,说明final的成员变量不 能被试图改变即使数值大小并没有改变。
例(2)
public class Test{
public static void main(String[] args){
A a=new A();
a.test(9);
}
}
class A{
final void test(final int i){
i=9;
}
}
编译报错:d:\Test.java:12: 不能指定最终参数 i
i=9;
说明:形参(局部变量)被final声明后不能被改变!
另:在类库中,很多类都是final的,例如:String、Math等。
说白了,被final声明后变成了只读的,不能被修改。
29、
在c++中,允许多继承,但是继承的多个父类之间可能会有重名的属性,所以会出现错误。
在java中只能允许单继承,这就避免了这种错误,虽然可以实现多个接口,但是接口里的属性是static的,所有的实例共享的。
图25
图26、27、28
分析以上的程序:接口也有多态性,如果一个类实现多个接口的话,用哪个接口声明(引用)就用哪个接口提供的方法(即别的接口提供的方法对其隐藏);
30、
接口可以继承接口,类继承类。
31、
图29
32、
图30
Error:是虚拟机出问题了,程序处理不了的;
Exception异常分为两种:RuntimeException和其他的,
其中RuntimeException可以不显式的用catch逮异常,因为很多异常比如数组溢出如果显式的逮的话很麻烦的;
其他的非RuntimeException必须显式的用catch去逮.
注意只要是RuntimeException就可以不逮,例如:
public class Test{
public static void main(String[] args){
A a=new A();
a.test(0);
}
}
class A{
void test( int i){
int j=4/i;
}
}
出错信息为:
图33
public static void main(String[] args){
A a=new A();
a.test(0);
}
}
class A{
void test( int i)throws Exception{
int j=4/i;
}
}
出错信息为:
图34
public static void main(String[] args){
A a=new A();
a.test(0);
}
}
class A{
void test( int i)throws RuntimeException{
int j=4/i;
}
}
出错信息为:
图35
因为算数异常是RuntimeException所以可以不处理,但是也可以显式的catch一下。
注意例如1和例如2的区别:例如1必须进行catch一下,否则无法编译。例如2中,打印的异常信息为两条,因为e.printStackTrace()包括出错的路径和调用的路径。
33、
图36、37、38
34、
在类库中的方法中后边如果有throws的话,调用此方法的时候一般都是需要catch的异常。
35、
图39、40
如果extends RuntimeException的话,说明自定义的异常可以不做处理。
36、
如果异常发生了,则后边的语句就不再执行了。
37、
图41
38、
图42
注意:java语言声明数组不能指定其长度,因为java分配在堆上边,例如:int a[4]是非法的;
在c语言中可以指定长度,在栈上分配4个小格来。
因为数组是引用类型,所以可以这样写int[] a=null;
数组的初始化和成员变量是一样的,例如下图:
图43
图44
39、
如果元素为引用数据类型的话,内存变化如下:
当执行Date[] days;后变化如下图:
图45
当执行days=new Date[3];后变化如下图:
图46
当执行完for循环后如下:
图47
40、
图48、49、50
41、
在局部变量中,如果是基本类型的话,会在栈中开辟空间,如果想在堆中开辟空间的话,用包装类型就行了。
42、
数组排序一:选择排序,即每次把第i位后边(包括第i位)最小的数放到第i位上,如下:
图51
但是这样效率并不高,因为每次找到比第i项小的数值的话就得和第i项做交换,而不是找到第i项以后最小的值才跟第i项交换,改进的方法如下:
图52
但是这样做的话,效率也不是最高的,因为每次外层循环都要在栈中重新开辟k、temp,循环结束后再弹出k、temp,改变以后如下:
图53
总结:冒泡和选择的巧计方法:冒泡是第二层循环体内数据的比较,选择是第一层数据和第二层数据的比较。
44、
数三退出问题:有500个人拉手围成一圈,从第1个人开始数数,如果数到3就退出,下个人继续从1开始数数,求最后剩下的人是谁?
图56
上述问题如果用面向对象的话:
图57、58、59
46、
二维数组
图62、63
47、
public class Test{
public static void main(String[] args){
int a[][]=new int[3][];
for(int i=0;i<a.length;i++)
{
System.out.print(a[i]+",");
}
}
}
结果为:
图64
说明:一维里存放的是对二维的引用。
48、
public class Test{
public static void main(String[] args){
int a[][]={{1},{22,3}};
for(int i=0;i<a.length;i++)
{
System.out.print(a[i]+",");
}
}
}
图65
49、
图66
如上图:一维里存的是对二维的引用,二维里存的是对字符串的引用。
50、
图67、68
在图67中,把数组s对字符串的引用copy了给数组sBak,这样sBak就会指向那些字符串了;
把数组intArray对二维的引用copy给了数组intArrayBak。
51、
图69
字符串在data seg里开辟空间,字符串一旦声明则不可改变;
注意s1==s2,因为他们是对同一个字符串的引用。
52、
图70、71、72
注意:trim是去掉字符串的开头和结尾的空间,字符串中间的空间不去掉!!
53、
图73
图74、75
54、
图76
55、
求字符串中大写字母、小写字母、非字母的个数
图77
56、
求字符串“java”在字符串中出现的次数
图80、81
58、
注意Integer.parseInt(s)和Integer.valueOf(s)的区别:前者返回int类型,后者Integer
即:后者的返回值=new Integer(Integer.parseInt(s))
59、
图87 88 89 90
60、
图91
61、
图93 94
62、
图95、96
注意:lastModified()方法返回值long类型的,因为计算机存的是从开始到现在的毫秒数。
63、
图97
注意:在linux里是“/”,但是在windows里“/”或者“\\”都行!!为什么是“\\”呢而不是“\”呢,因为“\\”是“\”的转移字符!!
把目录或文件按树形结构显示
图98
64、
枚举类型
图99
65、
容器
图100
Set:无顺序,不能重复
List:有顺序,能重复
什么是重复呢:就是两个对象之间互相equals。
图101、102
注意:add的时候add()必须add对象,不能add基本数据类型的数据,因为栈里的数据可能随时会清空。
上图中:System.out.println(c);会默认调用toString方法,而toString方法会调用里面每个数据的toString方法和“[]”。
66、
图103
如上图:
remove方法中,如果能在数据集里找到和它equals的实例,则remove,如果成功remove则返回true。
注意:new Integer(100).equals(new Integer(100))返回的是true,因为Integer重写了equals方法,如果里面的值相等的话就equals。
67、
图104
通过哈希码就很快找到对象,所以可以作为索引,所以重写equals方法的同时必须重写hasCode方法,因为两个对象equals的话,他们的hasCode也应该相等。那什么时候使用哈希码呢,当map的时候,找对象的时候按照索引(哈希码)找,而不必一个一个得比一下是不是equals。
图105
68、
图106
因为不同的容器存储方式不一样,所以遍历的方法不能统一,所以用不同容器来实例化Iterator接口。
图107
如上图:不能用c.remove(name)方法,因为Iterator使用的时候会把元素锁上,别的类用不了了。
69、
图108 109
70、
图110 111 112
71、
图113
ArrayList是底层用数组实现的,LinkedList是用链表实现的。
Object set()方法返回的是修改前的元素。
72、
图114 115
73、
图116 图117 118
lastName是字符串类型,String覆写了compareTo方法。
74、
图119 120
TreeMap:二叉树,做索引。
上边的键值不能重复是比较的equals,但是挨个排的比较equals的话太慢,所以比较哈希码(int类型)就行了。所以我们要求重写equals方法必须重写hasCode方法,如果连个对象equals的话那么他们的哈希值也相等,如果比较对象的话直接比较哈希值就行了。
Object put(Object,object)返回的是被替换的value.虽然第二个参数要求引用类型,但是由于自动打包的话,直接往里传基本数据类型就行,比如:m.put("one",1);
int i=(Integer)m.get("one");注意:不能去掉Integer,因为get的返回值是Object类型,只有Integer才会自动拆箱。
图121 122
75、
图123
如上图:第10、17行不用强制类型转换,这也是泛型的好处。
图125 126
所以在使用集合的时候最好使用泛型!!!
76、
图127 128 129 130 131 132
如上图:如果直接close的话,如果现在正在从缓存区里写数据的话,就会造成缓冲区里的内容可能就无法取出了。所以先flush一下。
注意:write(byte[] b)和read(byte[] b)比write(int b)和read(int b)性能高,write(byte[] b)与read(byte[] b)是每次都是操作一个数组级别的数,而不用每操作一个字节就和硬盘打交道一次。
77、
字节流有时候肯能会造成读一个汉字只读一一个字节即只读一个汉字的一半,所以可用用字符流。
图133 134 135 136
Writer类的write(String string)方法自动调用String类的 toCharArray()方法,然后调用write(char[] c)。
78、
图137 138 139 140 141
注意:系统只能自动建文件,不能自动建目录。
79、
图142 143 144 145
大管道套小管道
注意:readLine是BufferedReader的方法,每次读一行,很方便啊
80、
图146 147
ISO8859_x/latin-x:包含了所有的西欧语言。
81、
图148
System.in是这样定义的:public static final InputStream in
外部在套层InputStreamReader可以防止汉字乱码,再套层BufferReader可以使用readLine方法很方便。
82、
图149 150
ByteArrayOutputStream baos=new ByteArrayOutputSteam()这行执行后以后会在内存开辟一个字节数组,并且有个管道指向这个区域。
注意:先进先读
83、
图151 152 153 154
84、
图155
如果想把对象以字节的方式保存起来,得实现Serializable接口(标记接口,无方法)
图156
transient(透明的)即:不会把它声明的变量序列化。
Serializable是标记接口,jdk会控制怎么序列化。如果想控制序列化的话可以实现Externalizable接口。public interface Externalizable extends Serializable它是Serializable的子接口,自己有2个方法:
readExternal(ObjectInput in)
writeExternal(ObjectOutput out)
85、
线程
public class Thread extends Object implements Runnable:Runable接口只定义了run方法。
图157 158 159
如上图:run方法和main方法里的for循环是同时执行的!!!
图160
最好使用接口:因为extends就写死了,因为只能单继承,就不能继承其他类了。
86、
图161 162 163
如上图:不能在run()后边加上throw InterruptException,因为覆写不能抛出和父类方法不同的异常。
上图中程序的结果是:每个一秒输出一次时间,10秒后被打断,程序结束。
在Thread类中还有一个stop方法:和interrupt会让本线程执行完(比如说处理异常)不同,stop会立即终止线程。
87、
图164
如上图:join()使得run方法执行完,main中的for才执行。
图165
yield会让出让其他线程执行
如上图:共启动2个线程,每次是10的倍数的时候就会让给其他线程执行。
图166
如上图:在没有加第五行的时候,两个线程执行行数差不多,因为cpu分配的时间片是一样的。但是如果加上第五行的,即第一个线程的优先级是正常+3,所以第一个线程执行很多了才会执行第二个线程的。
88、
正常的让线程结束应该如下图,而不是用interrupt和stop方法。
图167
89、
图170
上图:结果是:t1,你是第2个使用timer的线程
t2,你是第2个使用timer的线程
造成这种的现象的原因就是:在t1这个线程执行num++后,执行24行之前,t2执行了num++。
解决方法如下:把注释去掉,synchronized锁定当前对象(互斥锁),不会别的对象打断。还有一种方法如下图:
图169
90、
请模拟死锁:
图171 172
上图中:线程td1抱着01等着o2,线程td2抱着o2等着o1。
91、
一道面试题:下图,如果线程t锁住m1(里面有b),问另一个线程(main)是否能访问m2(里面有b)。
图173
那么下图中的结果是多少呢?
图174
结果是:b=2000
那么下边的程序呢:
图175
结果是:b=1000
1000
也就是说:执行28行的时候,m2执行完把当前对象的锁释放掉,才能执行m1.
92、
生产者消费者问题
模拟如下:
图176 177 178 179
wait只能在synchronized的情况下使用。在线程阻塞时(即馒头吃完或者馒头生产的超过容量)使用wait。
notify和wait配套使用。如果是多个线程的话就必须用notifyAll了。
Object类中又一个wait(),在运行状态中,线程调用wait(),此时表示着线程将释放自己所有的锁标记,同时进入这个对象的等待队列。
等待队列的状态也是阻塞状态,只不过线程释放自己的锁标记。
Notify()
如果一个线程调用对象的notify(),就是通知对象等待队列的一个线程出列。进入锁池。如果使用notifyall()则通知等待队列中所有的线程出列。
那么sleep和wait有啥区别呢?
首先理解什么是锁标记:每个对象除了属性和方法,都有一个monitor(互斥锁标记),用来将这个对象交给一个线程,只有拿到monitor的线程才能够访问这个对象。
一、sleep属于Thread类的方法,wait是Object的方法
二、在运行状态中,线程调用wait(),此时表示着线程将释放自己所有的锁标记,同时进入这个对象的等待队列。等待队列的状态也是阻塞状态,只不过线程释放自己的锁标记。
而sleep是:
sleep是线程被调用时,占着cpu去睡觉(但是占用索标记即占着对象),其他线程不能占用cpu(不能访问对象),os认为该线程正在工作,不会让出系统资源(对象),wait是进入等待池等待,让出系统资源(释放锁标记),其他线程可以占用cpu,一般wait不会加时间限制,因为如果wait的线程运行资源不够,再出来也没用,要等待其他线程调用notifyall方法唤醒等待池中的所有线程,才会在进入就绪序列等待os分配系统资源,
93、
网络编程不等于网站编程!!!
网络编程比如:qq,魔兽争霸等
网站编程:http html css等
图180