Java复习——基础知识

对于 作者曹胜欢的专栏:Java程序员从笨鸟到菜鸟 https://blog.csdn.net/csh624366188/category_9260964.html?utm_source=zlmf2019 基本小结

基础知识

  1. Java以;结尾。
  2. Java的注释有: // 行注释 /* / 一行或多行注释 /* */ javadoc 注释
  3. Java合法命名规范: 大小写字母或者美元符号或者下划线开头,不能以数字开头,不能用关键字。类名一般大写开头,变量和方法一般小写开头。
  4. Java的基本数据类型:byte字节(8位)1字节 、boolean布尔(1位)、char字符(16位)两字节、short短整型(16位)两字节、int整形(32位)4字节、long长整型(64位)8字节、float单精度浮点型(32位)、double双精度浮点型(64位)
  5. Java的特殊引用类型: String
  6. 引用类型:引用类型是一个对象类型的,他的值指向内存空间的引用,就是地址,所指向的内存中保存着变量所表示的一个值或一组值。
  7. 定义变/常量和变量的初始化:常量需要用final 修饰 ,约定常量 一般全部使用大写字母。
  8. switch运算符: switch (expr) expr 必须是与int类型兼容的类型,即为byte,short,char,int其中的一个。
  9. 流程跳转语句-break:从switch 语句,循环语句或标号标识的代码块中退出
  10. 流程跳转语句-continue:跳出本次循环,执行下次循环。
  11. 流程跳转语句-return: 跳出本方法,跳到上层调用方法。
  12. 字符串是否相等用的是:equals;比较两个字符串的引用是否相等用“==”
  13. String,StringBuffer,StringBuild的区别

String 字符串常量
StringBuffer字符串变量(线程安全)
StringBuilder字符串变量(线程不安全)
简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:
String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
String S1 = “This is only a” + “ simple” + “test”; 其实就是:
String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
这时候 JVM 会规规矩矩的按照原来的方式去做
在大部分情况下 StringBuffer > String

  1. 类之间的三种关系:依赖关系(uses-a) 、聚集关系(has-a)汽车-轮子的关系、继承关系(is-a)老虎-动物的关系
  2. 面对对象:将复杂的事情简单化。面对对象将以前的过程中的执行者,变成了指挥者。面对对象这种思想是符合现在人们思考习惯的一种思想。
  3. 成员变量和局部变量的区别:

1、成员变量直接定义在类中。局部变量定义在方法中,参数上,语句中。
2、成员变量在这个类中有效。局部变量只在自己的大括号内有效
3、成员变量存在堆中,随着对象的产生而产生,消失而消失。局部变量存在于栈内存中,随着所属区域的运行而存在,结束而释放。

  1. 类的访问权限:public:对于成员和类来说:任何其他类都可访问 、默认:对于成员来说:同一个包内其他类可以访问、对于类来说:同一个包中的类可以使用。private:对于成员来说:只能在该成员隶属的类中访问。对于类来说:类不可以声明为private。 protected:对于成员来说:相同包中的类可以访问,基类通过protected把基类中的成员的访问权限赋予派生类不是所有类(派生类访问权限)
  2. 成员变量和静态变量的区别

1.成员变量所属于对象。 静态变量所属于类。
2. 成员变量存储在堆内存。静态彬良存在于方法区中。
3. 成员变量随着对象创建而存在。随着对象被回收而消失。静态变变量随着类的加载而存在。随着列的消失而消失。
4. 成员变量只能被对象所调用。静态变量可以被对象调用,也可以被类名调用。静态变量称为对象的共享数据

  1. 主函数的解释: 保证所在类的独立运行。是程序的入口。被JVM调用

public:访问权限最大。
static:不需要对象。直接类名即可。
void:主函数没有返回值。
main: 主函数特定的名称。
(String[] args):主函数的参数,是一个字符串数组类型的参数。jvm调用main方法时,传递的实际参数是 new String[0]。
jvm默认传递的是长度为0的字符串数组。我们在运行该类时,也可以指定具体的参数进行传递。可以在控制台,运行该类时,在后面加入参数。参数之间通过空格隔开。jvm会自动将这些字符串参数作为args数组中的元素,进行存储。
静态代码块、构造代码块、构造函数同时存在时的执行顺序:
静态代码块 --构造代码块 --构造函数;

  1. Java中除了static和final方法外,其他所有额方法都是运行时绑定的。在我另外一篇文章中所说到private方法都被隐式指定为final,因此final的方法不会在运行时绑定。当在派生类中重写基类中static、final、或private方法时,实质上是创建了一个新的方法。
  2. 在派生类中,对于基类中的private方法,最好采用不同的名字。
  3. 包含抽象方法的类叫做抽象类。注意定义里面包含这样的意思,只要列中包含一个抽象方法,该类就是抽象类。抽象类在派生类中就是基类的角色,为不同的子类提供的接口。
  4. 对象清理的顺序和创建的顺序相反,当然前提是自己想手动清理对象,因为大家都知道Java垃圾回收器。
  5. 比较抽象类与接口:
  1. 抽象类中可以为部分方法提供默认的实现,从而避免在子类中重复实现它们,这是抽象类的优势,但这一优势限制了多继承,而接口中只能包含抽象方法?。由于在抽象类中允许加入具体方法,因此扩展抽象类的功能,即向抽象类中添加具体方法,不会对它的子类造成影响,而对于接口,一旦被公布,就必须非常稳定,因为随意在接口中添加抽象方法,会影响到所有的实现类,这些实现类要么实现新增的抽象方法,要么声明为抽象类。
    总结:一个类即使有多个接口,也不会增加Java虚拟机进行动态绑定的复杂度。因为Java虚拟机永远不会把方法与接口绑定,而只会把方法与它的实现类绑定。
  2. 用接口作为系统与外界交互的窗口站在外界使用者(另一个系统)的角度,接口向使用者承诺系统能提供哪些服务,站在系统本身的角度,接口制定系统必须实现哪些服务,接口是胸痛中最高的抽象类型,通过接口交互可以提高两个系统之间的送耦合系统A通过B进行交互,是指系统A访问系统B时,把引用变量声明,为系统B中的接口类型,该引用变量引用系统B中接口的实现类的实例。
  3. Java接口本身必须非常稳定,Java接口一旦制定,就不允许随遇更改,否则对外面使用者及系统本身造成影响。
  1. Java中的包装类:

Java为基本类型提供包装类: Boolean、Char、Byte、Short、Int、Long、Float、Double
包装类带的共同的方法:
(1)带有基本值参数并创建包装类对象的构造函数.如可以利用Integer包 装类创建对象,Integer obj=new Integer(145);
(2)带有字符串参数并创建包装类对象的构造函数.如new Integer(“45”);
(3)生成字符串表示法的toString()方法,如obj.toString().
(4)对同一个类的两个对象进行比较的equals()方法,如obj1.eauqls(obj2);
(5)生成哈稀表代码的hashCode方法,如obj.hasCode();
(6)将字符串转换为基本值的 parseType方法,如Integer.parseInt(args[0]); 转化为 基本类型
(7)可生成对象基本值的typeValue方法,如obj.intValue(); 转化为基本类型
(8) valuleOf() 转换为包装类型

  1. 自动拆箱和自动装箱:根据jdk源码,java为了提高效率,IntegerCache类中有一个数组缓存 了值从-128到127的Integer对象。当我们调用Integer.valueOf(int i)的时候,如果i的值是>=-128且<=127时,会直接从这个缓存中返回一个对象,否则就new一个Integer对象。
  2. 数据流:

在这里插入图片描述

首先从字符流开始:

  1. 文件编码不同,而有了对字符进行高效操作的字符流对象。
    其实就是基于字节流读取字节时,去查了指定的码表。
    字节流和字符流的区别:
  • 字节流读取的时候,读取到一个字节就返回一个字节。字符流使用了字节流读取到一个或多个字节时,先去查找指定的编码表,将查到的字符返回。
  • 字节流可以处理所有类型数据,如图片,mp3,avi。而字符流只能处理字符数据。
    结论:只要是处理纯文本数据,就要先考虑使用字符流。而除此之外都用字节流。
  1. 基本的读写操作方式:
因为数据通常都是以文件的形式存在。
所以就要找到IO体系中可以用于操作文件的流对象。
通过名称可以更容易获取该对象,
因为IO体系同德子类名后缀绝大部分是父类名称。而前缀都是体现子类功能的名字。
Reader
   |--InputStreamReader
            |--FileReader:专门用于处理文件的字符读取流对象。
Writer
    |--OutputStreamWriter
                  |--FileWriter:专门用于处理文件的字符写入流对象。

**Reader中的常见的方法**:
-  int read() :读取一个字符。返回的是读取到的那个字符。读到最后返回-1.
- int read(char[]):将读到的字符存入指定的数组中,返回的是读到的字符个数,也就是往数组里装的元素的个数。如果读到流的末尾,返回-1.
- close():读取字符其实用的是window系统的功能,就希望使用完毕后,进行资源的释放
**Write中的常见方法**:
- write(ch): 将一个字符写入到流中。
- write(char[]): 将一个字符数组写入到流中。
- write(String): 将一个字符串写入到流中。
- flush():刷新流,将流中的数据刷新到目的地中,流还存在。
- close():关闭资源:在关闭前会先调用flush(),刷新流中的数据去目的地。然流关闭。
FileWriter:该类没有特有的方法只有自己的构造函数。该类特点在于
- 用于处理文本文件。
- 该类中有默认的编码表,
- 该类中有临时缓冲。
- 构造函数:在写入流对象初始化时,必须要有一个存储数据的目的地。
- 对于读取或者写入流对象的构造函数,以及读写方法,还有刷新关闭功能都会抛出IOException或其子类。所以都要进行处理。或者throws抛出,或者try catch处理
另一个小细节:
当指定绝对路径时,定义目录分隔符有两种方式:
1,反斜线但是一定要写两个。\\new FileWriter("c:\\demo.txt");
2,斜线/  写一个即可。new FileWriter("c:/demo.txt");
例子: 
FileReader fr = null;
try
{
fr = new FileReader("demo.txt");
char[] buf = new char[1024];//该长度通常都是1024的整数倍。
int len = 0;
while((len=fr.read(buf))!=-1)
{
System.out.println(new String(buf,0,len));
}
}
catch(IOException e)
{
System.out.println(e.toString());
}

字符的缓冲区:缓冲区的出现提高了对流的操作效率。
原理:其实就是将数组进行封装。
对应的对象:
BufferedWriter:特有方法:newLine():跨平台的换行符。
BufferedReader:特有方法:readLine():一次读一行,到行标记时,将行标记之前的字符数据作为字符串返回。当读到末尾时,返回null。
在使用缓冲区对象时,要明确,缓冲的存在是为了增强流的功能而存在,
所以在建立缓冲区对象时,要先有流对象存在。
其实缓冲内部就是在使用流对象的方法,只不过加入了数组对数据进行了临时存储。为了提高操作数据的效率。
代码上的体现:
写入缓冲区对象。
//建立缓冲区对象必须把流对象作为参数传递给缓冲区的构造函数。
BufferedWriter bufw = new BufferedWriter(new FileWriter("buf.txt"));
bufw.write("abce");//将数据写入到了缓冲区。
bufw.flush();//对缓冲区的数据进行刷新。将数据刷到目的地中。
bufw.close();//关闭缓冲区,其实关闭的是被包装在内部的流对象。
读取缓冲区对象。
BufferedReader bufr = new BufferedReader(new FileReader("buf.txt"));
String line = null;
//按照行的形式取出数据。取出的每一个行数据不包含回车符。
while((line=bufr.readLine())!=null)
{
System.out.println(line);
}
bufr.close();
readLine():方法的原理:
其实缓冲区中的该方法,用的还是与缓冲区关联的流对象的read方法。只不过,每一次读到一个字符,先不进行具体操作,先进行临时存储。当读取到回车标记时,将临时容器中存储的数据一次性返回。
既然明确了原理,我们也可以实现一个类似功能的方法。

然后说一下字节流:
抽象基类:InputStream,OutputStream。
字节流可以操作任何数据。
注意:字符流使用的数组是字符数组。char [] chs字节流使用的数组是字节数组。byte [] bt
FileOutputStream fos = new FileOutputStream("a.txt");
fos.write("abcde");//直接将数据写入到了目的地。
fos.close();//只关闭资源。
FileInputStream fis = new FileInputStream("a.txt");
//fis.available();//获取关联的文件的字节数。
//如果文件体积不是很大。
//可以这样操作。
byte[] buf = new byte[fis.available()];//创建一个刚刚好的缓冲区。
//但是这有一个弊端,就是文件过大,大小超出jvm的内容空间时,会内存溢出。
     fis.read(buf);

转化流:
特点:
- 是字节流和字符流之间的桥梁。
- 该流对象中可以对读取到的字节数据进行指定编码表的编码转换。
什么时候使用呢?
- 当字节和字符之间有转换动作时。
- 流操作的数据需要进行编码表的指定时。
具体的对象体现:
- InputStreamReader:字节到字符的桥梁。
- OutputStreamWriter:字符到字节的桥梁。
构造函数:
- InputStreamReader(InputStream):通过该构造函数初始化,使用的是本系统默认的编码表GBK。
- InputStreamReader(InputStream,String charSet):通过该构造函数初始化,可以指定编码表。
- OutputStreamWriter(OutputStream):通过该构造函数初始化,使用的是本系统默认的编码表GBK。
- OutputStreamWriter(OutputStream,String charSet):通过该构造函数初始化,可以指定编码表。
可以和流相关联的集合对象Properties.
Map
  |--Hashtable
     |--Properties
Properties:该集合不需要泛型,因为该集合中的键值对都是String类型。
1,存入键值对:setProperty(key,value);
2,获取指定键对应的值:value getProperty(key);
3,获取集合中所有键元素:
Enumeration  propertyNames();
在jdk1.6版本给该类提供一个新的方法。
Set<String> stringPropertyNames();
4,列出该集合中的所有键值对,可以通过参数打印流指定列出到的目的地。
list(PrintStream);
list(PrintWriter);
例:list(System.out):将集合中的键值对打印到控制台。
list(new PrintStream("prop.txt")):将集合中的键值对存储到prop.txt文件中。
5,可以将流中的规则数据加载进行集合,并称为键值对。
load(InputStream):
jdk1.6版本。提供了新的方法。
load(Reader):
注意:流中的数据要是"键=值" 的规则数据。
6,可以将集合中的数据进行指定目的的存储。
store(OutputStram,String comment)方法。
jdk1.6版本。提供了新的方法。
store(Writer ,String comment):
使用该方法存储时,会带着当时存储的时间。
  1. File类:该类的出现是对文件系统的中的文件以及文件夹进行对象的封装。

1,构造函数:
File(String filename):将一个字符串路径(相对或者绝对)封装成File对象,该路径是可存在的,也可以是不存在。
File(String parent,String child);
File(File parent,String child);
2,特别的字段:separator:跨平台的目录分隔符。
如:File file = new File(“c:”+File.separator+“abc”+File.separator+“a.txt”);
3,常见方法:
1,创建:
boolean createNewFile()throws IOException:创建文件,如果被创建的文件已经存在,则不创建。
boolean mkdir(): 创建文件夹。
boolean mkdirs(): 创建多级文件夹。
2,删除:
boolean delete():可用于删除文件或者文件夹。
注意:对于文件夹只能删除不带内容的空文件夹,
对于带有内容的文件夹,不可以直接删除,必须要从里往外删除。
void deleteOnExit(): 删除动作交给系统完成。无论是否反生异常,系统在退出时执行删除动作。

  1. Java反射机制主要提供了以下功能:
-  在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法
动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语
  1. java.lang.reflect包中
– Class类:代表一个类。
– Field 类:代表类的成员变量(成员变量也称为类的属性)。
– Method类:代表类的方法。
– Constructor 类:代表类的构造方法。
 –Array类:提供了动态创建数组,以及访问数组的元素的静态方法

  1. 代理 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问
•在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
代理模式一般涉及到的角色有
- 抽象角色:声明真实对象和代理对象的共同接口
- 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
- 真实角色:代理角色所代表的真实对象,是我们最终要引用的对象
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
Interface InvocationHandler:该接口中仅定义了一个方法
- public object invoke(Object obj,Method method, Object[] args)
  1. 索引类型:
1.唯一索引:唯一索引不允许两行具有相同的索引值

2.主键索引:为表定义一个主键将自动创建主键索引,主键索引是唯一索引的特殊类型。主键索引要求主键中的每个值是唯一的,并且不能为空

3.聚集索引(Clustered):表中各行的物理顺序与键值的逻辑(索引)顺序相同,每个表只能有一个

4.非聚集索引(Non-clustered):非聚集索引指定表的逻辑顺序。数据存储在一个位置,索引存储在另一个位置,索引中包含指向数据存储位置的指针。可以有多个,小于249个
  1. 索引的优缺点: 有点: 1. 加快访问速度 2. 加强行的唯一性 缺点:1. 带索引的表在数据库中需要更多的存储空间 2. 操作数据的命令需要更长的处理时间,因为它们需要对索引进行更新。
  2. 存储过程:存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。 存储过程(procedure)类似于Java语言中的方法
  3. 线程与进程的区别
多个进程的内部数据和状态都是完全独立的,而多线程是共享一块内存空间和一组系统资源,有可能互相影响. •线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。
 多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。从根本上说,这就是多线程编程的最终目的。
  1. java在多线程中的基础知识
- Java中如果我们自己没有产生线程,那么系统就会给我们产生一个线程(主线程,main方法就在主线程上运行),我们的程序都是由线程来执行的。
- 进程:执行中的程序(程序是静态的概念,进程是动态的概念)。 
- 线程的实现有两种方式,第一种方式是继承Thread类,然后重写run方法;第二种是实现Runnable接口,然后实现其run方法。 
- 将我们希望线程执行的代码放到run方法中,然后通过start方法来启动线程,start方法首先为线程的执行准备好系统资源,然后再去调用run方法。当某个类继承了Thread类之后,该类就叫做一个线程类。 
- 一个进程至少要包含一个线程。 
- 对于单核CPU来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来看,多个线程在同时执行(宏观并行)。 
- 对于双核或双核以上的CPU来说,可以真正做到微观并行。
  1. Thread源码研究:
- Thread类也实现了Runnable接口,因此实现了Runnable接口中的run方法; 
- 当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:Thread-number,该number将是自动增加的,并被所有的Thread对象所共享(因为它是static的成员变量)。 
- 当使用第一种方式来生成线程对象时,我们需要重写run方法,因为Thread类的run方法此时什么事情也不做。
- 当使用第二种方式生成线程对象时,我们需要实现Runnable接口的run方法,然后使用new Thread(new MyThread())(假如MyThread已经实现了Runnable接口)来生成线程对象,这时的线程对象的run方法或调就会MyThread类的run方法,这样我们自己编写的run方法就执行了。
  1. 两种生成线程对象的区别:
- 两种方法均需执行线程的start方法为线程分配必须的系统资源、调度线程运行并执行线程的run方法。 
- 在具体应用中,采用哪种方法来构造线程体要视情况而定。通常,当一个线程已继承了另一个类时,就应该用第二种方法来构造,即实现Runnable接口。 
  1. 线程的生命周期:
    在这里插入图片描述
- 创建状态:  创建状态的线程只是一个空的线程对象,系统不为它分配资源 
- 就绪状态: 执行start() 方法将相乘分配必须的系统资源,安排其运行,并调用线程体——run()方法,
- 运行状态: Running  运行状态。
- 挂起状态: Blocked 缺少资源,或者调用 sleep方法   需要  notify  或 notifyAll  方法去通知
- 停止状态: run 方法执行结束后自然消亡。
  1. 多线程并发
多线程并发是线程同步中比较常见的现象,java多线程为了避免多线程并发解决多线程共享数据同步问题提供了synchronized关键字
synchronized关键字:当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。 
- Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法再去访问该synchronized方法了,直到之前的那个线程执行方法完毕后(或者是抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法。 
- 如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。 
- 如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的对象所对应的Class对象,因为Java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行
- synchronized块,写法: 
synchronized(object) 
{ 
} 
- synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的代码是可以被多个线程同时访问到的。
  1. wait与notify:
- wait与notify方法都是定义在Object类中,而且是final的,因此会被所有的Java类所继承并且无法重写。这两个方法要求在调用时线程应该已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法或块当中。当线程执行了wait方法时,它会释放掉对象的锁。 
- 另一个会导致线程暂停的方法就是Thread类的sleep方法,它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的
- notify():唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待
- 直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。
此方法只应由作为此对象监视器的所有者的线程来调用。通过以下三种方法之一,线程可以成为此对象监视器的所有者:
- 通过执行此对象的同步实例方法。
- 通过执行在此对象上进行同步的 synchronized 语句的正文。
- 对于 Class 类型的对象,可以通过执行该类的同步静态方法。
- 一次只能有一个线程拥有对象的监视器。

在这里插入图片描述

 关于成员变量与局部变量:如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,他们对该成员变量是彼此影响的(也就是说一个线程对成员变量的改变会影响到另一个线程)。  如果一个变量是局部变量,那么每个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变不会影响到其他的线程。
  1. 死锁的问题:
    定义:线程1锁住了对象A的监视器,等待对象B的监视器,线程2锁住了对象B的监视器,等待对象A的监视器,就造成了死锁。
    导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。“synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性访问权。当线程访问对象时,线程会给对象加锁
    Java中每个对象都有一把锁与之对应。但Java不提供单独的lock和unlock操作。下面笔者分析死锁的两个过程“上锁”和“锁死” 。
上锁
     许多线程在执行中必须考虑与其他线程之间共享数据或协调执行状态,就需要同步机制。因此大多数应用程序要求线程互相通信来同步它们的动作,在 Java 程序中最简单实现同步的方法就是上锁。在 Java 编程中,所有的对象都有锁。线程可以使用 synchronized 关键字来获得锁。在任一时刻对于给定的类的实例,方法或同步的代码块只能被一个线程执行。这是因为代码在执行之前要求获得对象的锁。
    为了防止同时访问共享资源,线程在使用资源的前后可以给该资源上锁和开锁。给共享变量上锁就使得 Java 线程能够快速方便地通信和同步。某个线程若给一个对象上了锁,就可以知道没有其他线程能够访问该对象。即使在抢占式模型中,其他线程也不能够访问此对象,直到上锁的线程被唤醒、完成工作并开锁。那些试图访问一个上锁对象的线程通常会进入睡眠状态,直到上锁的线程开锁。一旦锁被打开,这些睡眠进程就会被唤醒并移到准备就绪队列中。
锁死
     如果程序中有几个竞争资源的并发线程,那么保证均衡是很重要的。系统均衡是指每个线程在执行过程中都能充分访问有限的资源,系统中没有饿死和死锁的线程。当多个并发的线程分别试图同时占有两个锁时,会出现加锁冲突的情形。如果一个线程占有了另一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。
 在编写多线程代码时,笔者认为死锁是最难处理的问题之一。因为死锁可能在最意想不到的地方发生,所以查找和修正它既费时又费力。
  1. 枚举: enum很像特殊的class,实际上enum声明定义的类型就是一个类。 而这些类都是类库中Enum类的子类(java.lang.Enum)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值