Java 基础(二)

1. 接口中所声明的方法都是抽象方法。接口中的方法都是public的。
2. 接口中也可以定义成员变量。 接口中的成员变量都是public、final、static的。
3. 一个类不能既是final,又是abstract的 。因为abstract的主要目的是定义一种约定,让子类去实现这种约定,而final表示该类不能被继承,这样abstract希望该类可以被继承而final明确说明该类不能被继承,两者矛盾。因此一个类不能既是final的,又是abstract的。
4. Design Pattern(设计模式)。单例模式(Singleton): 表示一个类只会生成唯一的一个对象。
以下为单例模式:

第二种:

 
 
 
5. 包(package)。用于将完成不同功能的类分门别类,放在不同的目录(包)下。包的命名规则:将公司域名反转作为包名。www.shengsiyuan.com,com.shengsiyuan(包名),对于包名: 每个字母都需要小写 。如果定义类的时候没有使用package,那么Java就认为我们所定义的类位于默认包里面(default package)。
6. 编译带有package声明的Java源文件有两种方式:
a) 直接编译,然后根据类中所定义的包名,逐一手工建立目录结构,最后将生成的class文件放到该目录结构中(很少使用,比较麻烦)。
b) 使用编译参数–d,方式为javac –d . 源文件.java,这样在编译后,编译器会自动帮助我们建立好包所对应的目录结构。
7. 有两个包名,分别是aa.bb.cc与aa.bb.cc.dd,那么我们称后者为前者的子包。
8. 导入(import),将使用package分离的各个类导入回来,让编译器能够找到所需要的类。
9. import的语法:import com.shengsiyuan.PackageTest;
10. import com.shengsiyuan.*,表示导入com.shengsiyuan包下面的所有类。
11. import aa.bb.*并不会导入aa.bb.cc包下面的类。这时需要这样写:
 
import aa.bb.*;
import aa.bb.cc.*;
12. 关于package、import、class的顺序问题:
a) 首先需要定义包(package),可选
b) 接下来使用import进行导入,可选
c) 然后才是class或interface的定义。
13. 如果两个类在同一个包下面,那么则不需要导入,直接使用即可。
14. 访问修饰符(access modifier)。
1) public(公共的):被public所修饰的属性和方法可以被所有类访问。
2) protected(受保护的):被protected所修饰的属性和方法可以在类内部、相同包以及 该类的子类所访问。
   子类不会继承父类中private的属性和方法
3) private(私有的):被private所修饰的属性和方法只能在该类内部使用
4) 默认的(不加任何访问修饰符):在类内部以及相同包下面的类所使用。
15. instanceof: 判断某个对象是否是某个类的实例。语法形式:引用名instanceof 类名(接口名),返回一个boolean值。
16. People people = new Man();
17. System.out.println(people instanceof People);//结果为true,因为Man是People的子类,根据继承,子类就是父类,因此Man也可以看作是People的实例。
18. 相等性的比较(==)
 
1) 对于原生数据类型来说,比较的是左右两边的值是否相等。
2) 对于引用类型来说,比较左右两边的引用 是否指向同一个对象,或者说左右两边的引用地址是否相同
19. java.lang.Object类。java.lang包在使用的时候无需显式导入,编译时由编译器自动帮助我们导入。
20. API (Application Programming Interface),应用编程接口。
21. 当打印引用时,实际上会打印出引用所指对象的toString()方法的返回值,因为每个类都直接或间接地继承自Object,而Object类中定义了toString(),因此每个类都有toString()这个方法。
22. 关于进制的表示:16进制,逢16进一,16进制的数字包括:0~9,A,B,C,D,E,F,
23. equals() 方法,该方法定义在 Object 类当中,因此 Java 中的每个类都具有该方法,对于 Object 类的 equals() 方法来说,它是判断调用 equals() 方法的引用与传进来的引用是否一致,即这两个引用是否指向的是同一个对象。对于 Object 类的 equals() 方法来说,它等价于 ==
24. 对于 String 类的 equals() 方法来说,它是判断当前字符串与传进来的字符串的内容是否一致。
25. 对于String对象的相等性判断来说,请使用equals()方法,而不要使用==。
26. String是常量,其对象一旦创建完毕就无法改变。 当使用+拼接字符串时,会生成新的String对象,而不是向原有的String对象追加内容
27. String Pool(字符串池)
28. String s = “aaa”;(采用字面值方式赋值)
1) 查找String Pool中是否存在“aaa”这个对象,如果不存在,则在String Pool中创建一个“aaa”对象,然后将String Pool中的这个“aaa”对象的地址返回来,赋给引用变量s,这样s会指向String Pool中的这个“aaa”字符串对象
2) 如果存在,则不创建任何对象,直接将String Pool中的这个“aaa”对象地址返回来,赋给s引用。
29. String s = new String(“aaa”);
1) 首先在String Pool中查找有没有“aaa”这个字符串对象,如果有,则不在String Pool中再去创建“aaa”这个对象了,直接在堆中(heap)中创建一个“aaa”字符串对象,然后将堆中的这个“aaa”对象的地址返回来,赋给s引用,导致s指向了堆中创建的这个“aaa”字符串对象。
2) 如果没有,则首先在String Pool中创建一个“aaa“对象,然后再在堆中(heap)创建一个”aaa“对象,然后将堆中的这个”aaa“对象的地址返回来,赋给s引用,导致s指向了堆中所创建的这个”aaa“对象。
 
 
Stringbuffer 可以改变的字符串,可以对本身追加内容,返回的是经过修改的本身。
最后通过tostring转换成字符串

1. 包装类(Wrapper Class)。针对于原生数据类型的包装。所有的包装类(8个)都位于java.lang包下。Java中的8个包装类分别是:Byte, Short, Integer, Long, Float, Double, Character, Boolean。他们的使用方式都是一样的,可以实现原生数据类型与包装类型的双向转换。
2. 数组(Array):相同类型数据的集合就叫做数组。
3. 如何定义数组。type[] 变量名= new type[数组中元素的个数];可以按照下列方式定义长度为10的数组:
 
int[] a = new int[10];或者int a[] = new int[10];
int[] a = {1,2,3};以及int[] a = new int[]{1,2,3};也是正确的
int[] a = new int[3]{1,2,3};是错误的
 
下面的输出结果为false,因为是创建了两个对象,所以不相等。

4. 数组中的元素索引是从 0开始的。对于数组来说,最大的索引 ==数组的长度 –1
5. 定义数组的第3种方式:type[] 变量名= {new type[]}{逗号分隔的初始化值列表};
6. Java中的 每个数组都有一个名为length的属性,表示数组的长度。length属性是public,final,int的。数组长度一旦确定,就不能改变大小。
7. int[] a = new int[10],其中a是一个引用,它指向了生成的数组对象的首地址,数组中每个元素都是int类型,其中仅存放数据值本身。
 
8. 二维数组。二维数组是一种平面的二维结构,本质上是数组的数组。二维数组的定义方式:type[][] a = new type[2][3];
type[][] a = new type[3][];是正确的
type[][] a = new type[][];是错误的
表示有3行,每行的列不同;然后分别为每个列来确定各自的行数,每一行分别代表一个一维数组。

type[][] a = new type[][3];是错误的
可以根据行来确定列,不能根据列还确定行
 
每一个小花括号里面的数组代表每一行的值,每一行的列数可以不同


循环遍历不规整的数组
 

不使用中间变量来交换两个变量的值

下图程序的输出结果为 B C
通过引用改变了对象的属性,所以A变为B,但是原生数据不会改变,所以为C
关键在于数组变量本身是一个引用。
 

 
通过方法,来交换两个变量的值,可以把两个变量放到数组中,通过交换数组来实现。
如果在方法中直接交换两个原生数据是不起作用的。

 
Array.equals(a,b)  可以比较两个数组a,b的内容是否相同
9. 三维数组。type[][][]a = new type[2][3][4];
10. 冒泡排序。
冒泡排序:升序

降序:
原理相同,只不过是把最小的先移动到最后
快速排序: 该方法的基本思想是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
11. 二分查找( Binary Search ):待查找的数组要有序。
 
12. 随机生成 50 个数字(整数),每个数字的范围是 [10, 50] ,统计每个数字出现的次数以及出现次数最多的数字与它的个数,最后将每个数字及其出现次数打印出来,如果某个数字出现次数为 0 ,则不要打印它。打印时按照数字的升序排列。
第二种方式:
 

生成10-50之间的随机数
Random random = new Random();
random.nextInt(41)+10;  random.nextInt(41)表示生成0-40之间的随机数
取范围的话,包括边界值中小的边界,不包括大的边界
 
1. 对于Java中的常量的命名规则 :所有单词的字母都是大写,如果有多个单词,那么使用下划线连接即可 。比如说:
 
public static final int AGE_0F_PERSON = 20;
2. 在Java中声明final常量时通常都会加上static关键字,这样对象的每个实例都会访问唯一一份常量值。
3. IDE(Integrated Development Environment),集成开发环境。
1) NetBeans。http://netbeans.org/,最高版本是6.9.1
2) JBuilder。
3) Intellij IDEA
4) Eclipse(日蚀、月蚀),最高版本3.6.1

4. 集合中存放的依然是对象的引用而不是对象本身。
5. ArrayList底层采用数组实现,当使用不带参数的构造方法生成ArrayList对象时,实际上会在底层生成一个长度为10的Object类型数组
6. 如果增加的元素个数超过了10个,那么ArrayList底层会新生成一个数组,长度为原数组的1.5倍+1,然后将原数组的内容复制到新数组当中,并且后续增加的内容都会放到新数组当中。当新数组无法容纳增加的元素时,重复该过程。
7. 对于ArrayList元素的删除操作,需要将被删除元素的后续元素向前移动,代价比较高。
8. 集合当中只能放置对象的引用,无法放置原生数据类型,我们需要使用原生数据类型的包装类才能加入到集合当中。
9. 集合当中放置的都是Object类型,因此取出来的也是Object类型,那么必须要使用强制类型转换将其转换为真正的类型(放置进去的类型)。
10. 关于ArrayList与LinkedList的比较分析
a ) ArrayList底层采用数组实现,LinkedList底层采用双向链表实现。
b) 当执行插入或者删除操作时,采用LinkedList比较好。
c) 当执行搜索操作时,采用ArrayList比较好。
 
 
1. 当向ArrayList添加一个对象时,实际上就是将该对象放置到了ArrayList底层所维护的数组当中; 当向LinkedList中添加一个对象时,实际上LinkedList内部会生成一个Entry对象 ,该Entry对象的结构为:
 
Entry
{
Entry previous;
Object element;
Entry next;
}
其中的Object类型的元素element就是我们向LinkedList中所添加的元素,然后Entry又构造好了向前与向后的引用previous、next,最后将生成的这个Entry对象加入到了链表当中。换句话说,LinkedList中所维护的是一个个的Entry对象。
2. 关于Object类的equals方法的特点
a) 自反性:x.equals(x)应该返回true
b) 对称性:x.equals(y)为true,那么y.equals(x)也为true。
c) 传递性:x.equals(y)为 true并且y.equals(z)为true,那么x.equals(z)也应该为true。
d) 一致性:x.equals(y)的第一次调用为true,那么x.equals(y)的第二次、第三次、第n次调用也应该为true,前提条件是在比较之间没有修改x也没有修改y。
e) 对于非空引用x,x.equals(null)返回false。
  TreeSet 的本质是一个 " 有序的,并且没有重复元素 " 的集合。 当我们构造 TreeSet 时;若使用不带参数的构造函数,则 TreeSet 的使用自然比较器;若用户需要使用自定义的比较器,则需要使用带比较器的参数。
 
 Iterator 顺序遍历
for (Iterator iter = set.iterator(); iter.hasNext(); ) {
    iter.next();
}
 
HashSet:元素并没有以某种特定顺序来存放;是无序的
LinkedHashSet :以元素插入的顺序来维护集合的链接表,允许以插入的顺序在集合中迭代;  
TreeSet:提供一个使用树结构存储Set接口的实现,对象以升序顺序存储,访问和遍历的时间很快。
 
 
 
3. 关于Object类的hashCode()方法的特点:
a) 在Java应用的一次执行过程当中,对于同一个对象的hashCode方法的多次调用,他们应该返回同样的值(前提是该对象的信息没有发生变化)。
b) 对于两个对象来说,如果使用equals方法比较返回true,那么这两个对象的hashCode值一定是相同的。
c) 对于两个对象来说,如果使用equals方法比较返回false,那么这两个对象的hashCode值不要求一定不同(可以相同,可以不同),但是如果不同则可以提高应用的性能。
d) 对于Object类来说,不同的Object对象的hashCode值是不同的(Object类的hashCode值表示的是对象的地址)。
4. 当使用HashSet时,hashCode()方法就会得到调用,判断已经存储在集合中的对象的hash code值是否与增加的对象的hash code值一致;如果不一致,直接加进去;如果一致,再进行equals方法的比较,equals方法如果返回true,表示对象已经加进去了,就不会再增加新的对象,否则加进去。
5. 如果我们重写equals方法,那么也要重写hashCode方法,反之亦然。
 
重写equals和hashcode方法,来实现如果对象的某个属性相同,则不能加入集合
判断Student类的name属性是否相同
 
 
遍历获取hashset中的每个元素
 
 
6. Map(映射):Map的keySet()方法会返回key的集合,因为Map的键是不能重复的,因此keySet()方法的返回类型是Set;而Map的值是可以重复的,因此values()方法的返回类型是Collection,可以容纳重复的元素。
 
HashMap里面的元素是无序的。如果键相同,则后面的值会覆盖掉前面的值。
 
遍历hashmap的方式:
第一种:(先拿键,再拿值)
第二种:(一起拿,然后分别取出键和值)
 
Collections 工具类提供了大量针对 Collection/Map 的操作。
1. 排序操作(主要针对 List 接口相关)
·          reverse(List list) :反转指定 List 集合中元素的顺序
·          shuffle(List list) :对 List 中的元素进行随机排序(洗牌)
·          sort(List list) :对 List 里的元素根据自然升序排序
2. 查找和替换(主要针对 Collection 接口相关)
·          binarySearch(List list, Object key) :使用二分搜索法,以获得指定对象在 List 中的索引,前提是集合已经排序
·          max(Collection coll) :返回最大元素
3. 同步控制
4. 设置不可变集合
 

 
2. HashSet底层是使用HashMap实现的。当使用add方法将对象添加到Set当中时,实际上是将该对象作为底层所维护的Map对象的key,而value则都是同一个Object对象(该对象我们用不上);
3. HashMap底层维护一个数组,我们向HashMap中所放置的对象实际上是存储在该数组当中;
4. 当向HashMap中put一对键值时,它会根据key的hashCode值计算出一个位置,该位置就是此对象准备往数组中存放的位置。
5. 如果该位置没有对象存在,就将此对象直接放进数组当中;如果该位置已经有对象存在了,则顺着此存在的对象的链开始寻找(Entry类有一个Entry类型的next成员变量,指向了该对象的下一个对象),如果此链上有对象的话,再去使用equals方法进行比较,如果对此链上的某个对象的equals方法比较为false,则将该对象放到数组当中,然后将数组中该位置以前存在的那个对象链接到此对象的后面。
6. HashMap的内存实现布局:
7. 所谓泛型:就是变量类型的参数化。
 
 
掌握以下知识点:

当遍历集合或数组时,如果需要访问集合或数组的下标,那么最好使用旧式的方式来实现循环或遍历,而不要使用增强的for循环,因为它丢失了下标信息。
增强for循环
/* 建立一个数组 */
  int [] integers = { 1 2 3 4 };
 
  /* 开始遍历 */
  for ( int i : integers) {
     System. out .println(i); /* 依次输出 “1” “2” “3” “4” */
 }
 
2. Integer类有一个缓存,它会缓存介于-128~127之间的整数。
3. 可变参数:可变参数本质上就是一个数组,对于某个声明了可变参数的方法来说,我们既可以传递离散的值,也可以传递数组对象。但如果将方法中的参数定义为数组,那么只能传递数组对象而不能传递离散的值。
4. 可变参数必须要作为方法参数的最后一个参数,即一个方法不可能具有两个或两个以上的可变参数。
5. 枚举(Enum):我们所定义的每个枚举类型都继承自java.lang.Enum类,枚举中的每个成员默认都是public static final的。
6. 而每个枚举的成员其实就是您定义的枚举类型的一個实例(Instance)。换句话说,当定义了一个枚举类型后,在编译时刻就能确定该枚举类型有几个实例,分别是什么。在运行期间我们无法再使用该枚举类型创建新的实例了,这些实例在编译期间就已经完全确定下来了。
7. 静态导入:
a) import static com.shengsiyuan.common.Common.Age;
b) import static com.shengsiyuan.common.Common.output;
表示导入Common类中的静态成员变量AGE以及静态方法output。注意:使用 import static时,要一直导入到类中的静态成员变量或静态方法。
8. Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象。
 
 
反射:详见文档 反射
 
1. 要想使用反射,首先需要获得待处理类或对象所对应的Class对象。
2. 获取某个类或某个对象所对应的 Class 对象的常用的 3 种方式:
a) 使用Class类的静态方法forName:Class.forName(“java.lang.String”);
b) 使用类的.class语法:String.class;
c) 使用对象的getClass()方法:String s = “aa”; Class<?> clazz = s.getClass();
3. 若想通过类的不带参数的构造方法来生成对象,我们有两种方式:
a) 先获得Class对象,然后通过该Class对象的newInstance()方法直接生成即可:
 
Class<?> classType = String.class;
Object obj = classType.newInstance();
b) 先获得Class对象,然后通过该对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成:
 
Class<?> classType = Customer.class;
Constructor cons = classType.getConstructor(new Class[]{});
Object obj = cons.newInstance(new Object[]{});
4. 若想通过类的带参数的构造方法生成对象,只能使用下面这一种方式:
 
Class<?> classType = Customer.class;
Constructor cons = classType.getConstructor(new Class[]{String.class, int.class});
Object obj = cons.newInstance(new Object[]{“hello”, 3});
5. Integer.TYPE返回的是int,而Integer.class返回的是Integer类所对应的Class对象。
 
 
 
对InvokeTester类使用反射,调用add(int a,int b)和echo(String s)方法
 
动态代理模式: 看懂代码示例  dynamicproxy代码  VectorProxy代码
参考文档: 代理模式
 

2. Java注解(Annotation):
a) Override注解表示子类要重写(override)父类的对应方法。
b) Deprecated注解表示方法是不建议被使用的。
c) SuppressWarnings注解表示抑制警告。
3. 自定义注解:当注解中的属性名为value时,在对其赋值时可以不指定属性的名称而直接写上属性值即可;除了value以外的其他值都需要使用name=value这种赋值方式,即明确指定给谁赋值。
4. 当我们使用@interface关键字定义一个注解时,该注解隐含地继承了java.lang.annotation.Annotation接口;如果我们定义了一个接口,并且让该接口继承自Annotation,那么我们所定义的接口依然还是接口而不是注解;Annotation本身是接口而不是注解。可以与Enum类比。
 
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
 
5. JUnit(3.8、4.x):Keep the bar green to keep the code clean.
6. 我的名言:没有反射,很多框架就不存在了。(No Reflection,No most frameworks)。
7. JUnit4的执行的一般流程:
a) 首先获得待测试类所对应的Class对象。
b) 然后通过该Class对象获得当前类中所有public方法所对应的Method数组。
c) 遍历该Method数组,取得每一个Method对象
d) 调用每个Method对象的isAnnotationPresent(Test.class)方法,判断该方法是否被Test注解所修饰。
e) 如果该方法返回true,那么调用method.invoke()方法去执行该方法,否则不执行。
8. 单元测试不是为了证明你是对的,而是证明你没有错误。
9. Writing Secure Code(编写安全的代码):Input is evil。
10. 异常(Exception)。
11. Java中的异常分为两大类:
a) Checked exception (非 Runtime Exception)
b) Unchecked exception(Runtime Exception)
12. Java中所有的异常类都会直接或间接地继承自Exception。
13. RuntimeException类也是直接继承自Exception类,它叫做运行时异常,Java中所有的运行时异常都会直接或间接地继承自RuntimeException。
14. Java中凡是继承自Exception而不是继承自RuntimeException的类都是非运行时异常。
15. 异常处理的一般结构是:
 
try
{
}
catch(Exception e)
{
}
finally
{
}
无论程序是否出现异常,finally块中的代码都是会被执行的。
16. 对于非运行时异常(checked exception),必须要对其进行处理, 处理方式有两种:第一种是使用try.. catch…finally进行捕获;第二种是在调用该会产生异常的方法所在的方法声明throws Exception
17. 对于运行时异常(runtime exception),我们可以不对其进行处理,也可以对其进行处理。推荐不对其进行处理。
18. NullPointerException是空指针异常,出现该异常的原因在于某个引用为null,但你 却调用了它的某个方法。这时就会出现该异常。
 
 
 
 Java SE Lesson 11
1. 所谓自定义异常,通常就是定义了一个继承自Exception类的子类,那么这个类就是一个自定义异常类。通常情况下,我们都会直接继承自Exception类,一般不会继承某个运行时的异常类。
2. 我们可以使用多个catch块来捕获异常,这时需要将父类型的catch块放到子类型的catch块之后,这样才能保证后续的catch可能被执行,否则子类型的catch将永远无法到达,Java编译器会报编译错误; 如果多个catch块的异常类型是独立的(MyException, MyException2), 那么谁前谁后都是可以的。
3. 如果try块中存在return语句,那么首先也需要将finally块中的代码执行完毕,然后方法再返回。
4. 如果try块中存在System.exit(0)语句,那么就不会执行finally块中的代码,因为System.exit(0)会终止当前运行的Java虚拟机,程序会在虚拟机终止前结束执行。
5. GUI(Graphical User Interface),图形用户界面。
6. AWT(Abstract Window Toolkit),抽象窗口工具集,第一代的Java GUI组件,是重量级的。
7. Swing,不依赖于底层细节,轻量级的组件。
 
 
  Java SE Lesson 13
1. 内部类(Inner Class),内部类共分为4种。
2. 静态内部类(static inner class):只能访问外部类的静态成员变量与静态方法,生成静态内部类对象的方式为:
 
OuterClass.InnerClass inner = new OuterClass.InnerClass();
3. 成员内部类(member inner class):可以访问外部类的静态与非静态的方法与成员变量。生成成员内部类对象的方式为:
 
OuterClass.InnerClass inner = new OuterClass().new InnerClass();
4. 若想在局部内部类中访问外部类的成员变量,语法为:OuterClass.this.a;
5. 局部内部类(Local Inner Class):定义在方法当中,只能访问方法中声明的final类型的变量。
6. 匿名内部类(Anonymous Inner Class):匿名内部类会隐式地继承一个父类或实现一个接口。
 
参考文档: 文件操作
 
要想在磁盘目录创建文件,则该目录必须存在。
 
在Windows下,把Windows磁盘目录中的\在代码中改为/
 
从外部流入程序,称为输入
从程序流出到磁盘,称为输出
 
7. 所谓递归(Recursion),就是方法调用自身。对于递归来说,一定有一个出口,让递归结束,只有这样才能保证不出现死循环。
8. 作业:给定任意一个目录,以树形方式展现出该目录中的所有子目录和文件。另外,在展现的时候将目录排在上面,文件排在下面。每一层要加上缩进。
  
左边的为统一的接口,图中间的为节点流,右边的为过滤流
 

 
字节流读文件的标准写法:
 
字节流写文件:覆盖内容
追加文件内容,修改为
 
字符流读文件
 
字符流写文件
 
字符流写文件
Java SE Lesson 15
 
1. 一个类若想被序列化,则需要实现java.io.Serializable 接口,该接口中没有定义任何
方法,是一个标识性接口(Marker Interface),当一个类实现了该接口,就表示这个
类的对象是可以序列化的。
2. 在序列化时,static 变量是无法序列化的;如果A 包含了对B 的引用,那么在序列化
A 的时候也会将B 一并地序列化;如果此时A 可以序列化,B 无法序列化,那么当
序列化A 的时候就会发生异常,这时就需要将对B 的引用设为transient,该关键字
表示变量不会被序列化。
3. 当我们在一个待序列化/反序列化的类中实现了以上两个private 方法(方法声明要
与上面的保持完全的一致),那么就允许我们以更加底层、更加细粒度的方式控制序
列化/反序列化的过程。
4. Java 中如果我们自己没有产生线程,那么系统就会给我们产生一个线程(主线程,
main 方法就在主线程上运行),我们的程序都是由线程来执行的。
5. 进程:执行中的程序(程序是静态的概念,进程是动态的概念)。
6. 线程的实现有两种方式, 第一种方式是继承Thread 类,然后重写run 方法;第二种
是实现Runnable 接口,然后实现其run 方法
7. 将我们希望线程执行的代码放到run 方法中,然后通过start 方法来启动线程,start
方法首先为线程的执行准备好系统资源,然后再去调用run 方法。当某个类继承了
Thread 类之后,该类就叫做一个线程类。
8. 一个进程至少要包含一个线程。
9. 对于单核CPU 来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来
看,多个线程在同时执行(宏观并行)。
 
在多线程中,各个线程的执行次序是不确定的。
必须调用start方法,不能调用run,否则会当成普通的类而不是线程
 
10. 对于双核或双核以上的CPU 来说,可以真正做到微观并行。
11.
1) Thread 类也实现了Runnable 接口,因此实现了Runnable 接口中的run 方法;
2) 当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下
形式:Thread-number,该number 将是自动增加的,并被所有的Thread 对象所共享
(因为它是static 的成员变量)。
3) 当使用第一种方式来生成线程对象时,我们需要重写run 方法,因为Thread 类的run
方法此时什么事情也不做。
4) 当使用第二种方式来生成线程对象时,我们 需要实现Runnable接口的run方法 ,然后使用new Thread(new MyThread())(假如MyThread已经实现了Runnable接口)来生成线程对象,这时的线程对象的run方法就会调用MyThread类的run方法,这样我们自己编写的run方法就执行了。
Runnable接口中有抽象的run方法
停止线程不能使用stop方法
 
线程生命周期
12. 关于成员变量与局部变量:如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,他们对该成员变量是彼此影响的(也就是说一个线程对成员变量的改变会影响到另一个线程)。
13. 如果一个变量是局部变量,那么每个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变不会影响到其他的线程。
14. 停止线程的方式:不能使用Thread类的stop方法来终止线程的执行。一般要设定一个变量,在run方法中是一个循环,循环每次检查该变量,如果满足条件则继续执行,否则跳出循环,线程结束。
15. 不能依靠线程的优先级来决定线程的执行顺序。
16. synchronized关键字:当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
17. * * Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时, 表示将该对象上锁 ,此时其他任何线程都无法再去访问该synchronized方法了,直到之前的那个线程执行方法完毕后(或者是抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法。
 
18. 如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。因为synchronized方法是给对象上锁的。
 
 
  Java SE Lesson 16
1. 如果某个synchronized方法是static的,那么当线程访问该方法时, 它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的对象所对应的Class对象,因为Java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象, 因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。
2. synchronized块,写法:
 
synchronized(object)
{
}
表示线程在执行的时候会对object对象上锁。
3. synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的代码是可以被多个线程同时访问到的。
4. 死锁(deadlock)
5. wait与notify方法都是定义在Object类中,而且是final的,因此会被所有的Java类所继承并且无法重写。这两个方法要求在调用时线程应该已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法或块当中。 当线程执行了wait方法时,它会释放掉对象的锁。
6. 另一个会导致线程暂停的方法就是Thread类的sleep方法,它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。
 

Object类的clone方法为浅复制
 
1、对所引用的对象也复制一份,就变成了深复制
 
先重写引用对象的clone方法,在重新获取克隆后的引用对象
 

 
2、使用序列化的方式实现深复制
 
   先写入流,再读出来

 
 

 
 
 
 
 
 
 
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值