Skip to content

Latest commit

 

History

History
794 lines (509 loc) · 33.7 KB

JavaBasicSegment.md

File metadata and controls

794 lines (509 loc) · 33.7 KB

java basic segment

目录

if, else

if和switch语句很像。具体什么场景下,应用哪个语句呢?

如果判断的具体数值不多,而是符合byte short int char这四种类型。虽然两个语句都可以使用,建议使用swtich语句。因为效率稍高。

其他情况:对区间判断,对结果为boolean类型判断,使用if,if的使用范围更广。

类和对象的关系

  • 现实生活中的对象:张三 李四。
  • 想要描述:提取对象中共性内容。对具体的抽象。
  • 描述时:这些对象的共性有:姓名,年龄,性别,学习java功能。
  • 映射到java中,描述就是class定义的类。
  • 具体对象就是对应java在堆内存中用new建立实体。
  • 类就是:对现实生活中事物的描述。
  • 对象:就是这类事物,实实在在存在个体。

成员变量和局部变量

  • 作用范围
    • 成员变量作用于整个类中。
    • 局部变量变量作用于函数中,或者语句中。
  • 在内存中的位置
    • 成员变量:在堆内存中,因为对象的存在,才在内存中存在。
    • 局部变量:存在栈内存中。

private

私有,权限修饰符:用于修饰类中的成员(成员变量,成员函数)。私有只在本类中有效。

将age私有化以后,类以外即使建立了对象也不能直接访问。 但是人应该有年龄,就需要在Person类中提供对应访问age的方式。

注意:私有仅仅是封装的一种表现形式。 之所以对外提供访问方式,就因为可以在访问方式中加入逻辑判断等语句。 对访问的数据进行操作。提高代码健壮性。

构造函数

对象一建立就会调用与之对应的构造函数。 构造函数的作用:可以用于给对象进行初始化。

构造函数的小细节

  • 当一个类中没有定义构造函数时,那么系统会默认给该类加入一个空参数的构造函数。
  • 当在类中自定义了构造函数后,默认的构造函数就没有了。

构造函数和一般函数在写法上有不同。在运行上也有不同。构造函数是在对象一建立就运行。给对象初始化。

而一般方法是对象调用才执行,给是对象添加对象具备的功能。

一个对象建立,构造函数只运行一次。而一般方法可以被该对象调用多次。

什么时候定义构造函数呢? 当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中。

this

this:看上去,是用于区分局部变量和成员变量同名情况。 this为什么可以解决这个问题?this到底代表的是什么呢?

this:就代表本类的对象,到底代表哪一个呢? this代表它所在函数所属对象的引用。简单说:哪个对象在调用this所在的函数,this就代表哪个对象

this的应用:当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。

但凡本类功能内部使用了了本类对象,都用this表示。

this语句用于构造函数之间进行互相调用。this语句只能定义在构造函数的第一行。因为初始化要先执行。

静态的应用

静态:static。

用法:是一个修饰符,用于修饰成员(成员变量,成员函数). 当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外, 还可以直接被类名调用。类名.静态成员。

static特点:

  1. 随着类的加载而加载。也就说:静态会随着类的消失而消失。说明它的生命周期最长。
  2. 优先于的对象存在明确一点:静态是先存在。对象是后存在的。
  3. 被所有对象所共享
  4. 可以直接被类名所调用。

实例变量和类变量的区别:

  1. 存放位置。类变量随着类的加载而存在于方法区中。实例变量随着对象的建立而存在于堆内存中。
  2. 生命周期.类变量生命周期最长,随着类的消失而消失。实例变量生命周期随着对象的消失而消失。

静态使用注意事项

  1. 静态方法只能访问静态成员。非静态方法既可以访问静态也可以访问非静态。
  2. 静态方法中不可以定义this,super关键字。因为静态优先于对象存在。所以静态方法中不可以出现this。
  3. 主函数是静态的。

静态有利有弊

  • 利处:对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份。可以直接被类名调用。
  • 弊端:生命周期过长。访问出现局限性。(静态虽好,只能访问静态。)

每一个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装。以便复用。

虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作。发现了问题:

  1. 对象是用于封装数据的,可是ArrayTool对象并未封装特有数据。
  2. 操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。

这时就考虑,让程序更严谨,是不需要对象的。可以将ArrayTool中的方法都定义成static的。 直接通过类名调用即可。

将方法都静态后,可以方便于使用,但是该类还是可以被其他程序建立对象的。 为了更为严谨,强制让该类不能建立对象。可以通过将构造函数私有化完成。

接下来,将ArrayTool.class文件发送给其他人,其他人只要将该文件设置到classpath路径下, 就可以使用该工具类。但是,很遗憾,该类中到底定义了多少个方法,对方去不清楚。 因为该类并没有使用说明书。开始制作程序的说明书。java的说明书通过文档注释来完成。

主函数

public static void main(String[] args)

主函数:是一个特殊的函数。作为程序的入口,可以被jvm调用。

主函数的定义:

  • public:代表着该函数访问权限是最大的。
  • static:代表主函数随着类的加载就已经存在了。
  • void:主函数没有具体的返回值。
  • main:不是关键字,但是是一个特殊的单词,可以被jvm识别。
  • (String[] arr):函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型的数组。

主函数是固定格式的:jvm识别,jvm在调用主函数时,传入的是new String[0];

单例

设计模式:解决某一类问题最行之有效的方法。 java中23种设计模式: 单例设计模式:解决一个类在内存只存在一个对象。

想要保证对象唯一

  1. 为了避免其他程序过多建立该类对象。先禁止其他程序建立该类对象
  2. 还为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。
  3. 为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。

这三部怎么用代码体现呢?

  1. 将构造函数私有化。
  2. 在类中创建一个本类对象。
  3. 提供一个方法可以获取到该对象。

对于事物该怎么描述,还怎么描述。 当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。

// 恶汉式
class Singleton {
	private  Singleton(){}
	private static Singleton instance = new Singleton();
	public static  Singleton getInstance() {
		return instance;
	}
}

// 懒汉式
class Singleton {
	private static Singleton instance = null;
	private Singleton(){}
	public static Singleton getInstance() {
		if(s==null)
			synchronized(Singleton.class) {				
				if(s==null)
					instance = new Singleton();
			}
		return instance;
	}
}

抽象

当多个类中出现相同功能,但是功能主体不同, 这是可以进行向上抽取。这时,只抽取功能定义,而不抽取功能主体。

抽象:看不懂。

抽象类的特点

  1. 抽象方法一定在抽象类中。
  2. 抽象方法和抽象类都必须被abstract关键字修饰。
  3. 抽象类不可以用new创建对象。因为调用抽象方法没意义。
  4. 抽象类中的抽象方法要被使用,必须由子类复写起所有的抽象方法后,建立子类对象调用。如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。

抽象类和一般类没有太大的不同。该如何描述事物,就如何描述事物,只不过,该事物出现了一些看不懂的东西。 这些不确定的部分,也是该事物的功能,需要明确出现。但是无法定义主体。 通过抽象方法来表示。

抽象类比一般类多个了抽象函数。就是在类中可以定义抽象方法。抽象类不可以实例化。

特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。

abstract 关键字,和哪些关键字不能共存?

  • final:被final修饰的类不能有子类。而被abstract修饰的类一定是一个父类。
  • private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。而抽象方法出现的就是需要被复写。
  • static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法运行没意义。

抽象类中是否有构造函数?

有,抽象类是一个父类,要给子类提供实例的初始化。

继承

Java语言中:java只支持单继承,不支持多继承。

因为多继承容易带来安全隐患:当多个父类中定义了相同功能, 当功能内容不同时,子类对象不确定要运行哪一个。 但是java保留这种机制。并用另一种体现形式来完成表示。多实现。

java支持多层继承。也就是一个继承体系

如何使用一个继承体系中的功能呢? 想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中共性功能。 通过了解共性功能,就可以知道该体系的基本功能。 那么这个体系已经可以基本使用了。

那么在具体调用时,要创建最子类的对象,为什么呢? 一是因为有可能父类不能创建对象, 二是创建子类对象可以使用更多的功能,包括基本的也包括特有的。 简单一句话:查阅父类功能,创建子类对象使用功能。

子父类出现后,类成员的特点:

类中成员

  1. 变量

    如果子类中出现非私有的同名成员变量时,子类要访问本类中的变量,用this子 类要访问父类中的同名变量,用super。

    super的使用和this的使用几乎一致。this代表的是本类对象的引用。super代表 的是父类对象的引用。

  2. 函数

    当子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数 的内容。如同父类的函数被覆盖一样。这种情况是函数的另一个特性:重写(覆盖)

    当子类继承父类,沿袭了父类的功能,到子类中,但是子类虽具备该功能,但是 功能的内容却和父类不一致,这时,没有必要定义新功能,而是使用覆盖特殊,保 留父类的功能定义,并重写功能内容。

    覆盖:

    1. 子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖,否则编译失败
    2. 静态只能覆盖静态。

    重载:只看同名函数的参数列表。 重写:子父类方法要一模一样。

  3. 构造函数

    在对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的构造函 数默认第一行有一条隐式的语句super();super():会访问父类中空参数的构造函数 。而且子类中所有的构造函数默认第一行都是super();

    为什么子类一定要访问父类中的构造函数?因为父类中的数据子类可以直接获取。 所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。 所以子类在对象初始化时,要先访问一下父类中的构造函数。如果要访问父类中 指定的构造函数,可以通过手动定义super语句的方式来指定。

    注意:super语句一定定义在子类构造函数的第一行。

    子类的实例化过程结论:

    子类的所有的构造函数,默认都会访问父类中空参数的构造函数。 因为子类每一个构造函数内的第一行都有一句隐式super();

    当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访 问父类中的构造函数。

    当然:子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。 子类中至少会有一个构造函数会访问父类中的构造函数。

final

final : 最终。作为一个修饰符,

  1. 可以修饰类,函数,变量。

  2. 被final修饰的类不可以被继承。为了避免被继承,被子类复写功能。

  3. 被final修饰的方法不可以被复写。

  4. 被final修饰的变量是一个常量只能赋值一次,既可以修饰成员变量,有可以修饰局部变量。

    当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给 这些值起个名字。方便于阅读。而这个值不需要改变,所以加上final修饰。作为 常量:常量的书写规范所有字母都大写,如果由多个单词组成。单词间通过_连接

  5. 内部类定义在类中的局部位置上是,只能访问该局部被final修饰的局部变量。

抽象类和接口

抽象类:其实就是在分析事物时,事物中的功能有些是不明确的内容的。这些不明确内容就是抽象的。可以通过抽象函数来描述。

抽象函数一定要定义在抽象类中,因为,抽象函数所在类,也必须被抽象标识。写法特点:

  1. 抽象函数只对函数进行声明,没有函数主体。
  2. 抽象类和抽象函数都需要用abstract修饰。
  3. 抽象类不可以进行实例化。
  4. 想要使用抽象功能,必须通过子类覆盖了父类中所有的抽象方法后,才可以对子类实例化。

如果只覆盖了部分抽象方法,那么子类还是一个抽象类。也可以理解为:抽象类是一个父类,是不断向上抽取而来的,在抽取过程中,只抽取了方法声明,但没有抽取方法实现。抽象类和一半类差不多。区别:抽象类可以定义抽象方法。抽象类不可以建立对象。其实抽象类一样用于描述事物,既可以定义抽象方法,也可以定义非抽象方法。

接口:接口看上去是一个特殊的抽象类。里面存的都是抽象方法。

格式:

  1. 通过interface来定义。
  2. 接口中常见成员:常量,抽象方法。而且这些成员都有固定的修饰符。常量:public static final,方法:public abstract
  3. 接口中的成员都是共有的。
  4. 一个类可以对接口进行多实现,也弥补了多继承带来的安全隐患,所以java对多继承进行了改良。用多实现方法来体现多继承的特性。
  5. 一个类可以继承一个类的同时,实现多个接口。
  6. 接口与接口之间是继承关系,而且可以多继承。

应用特点:

  1. 接口是对外暴露的规则。
  2. 接口是功能的扩展。
  3. 接口的出现降低了耦合性。别忘了说的时候,需要举例。如usb。pci,主板。插

抽象类和接口异同:

相同:

  1. 都可以在内部定义抽象方法。
  2. 通常都在顶层。
  3. 都不可以实例化,都需要子类来实现。

不同点:

  1. 抽象类中可以定义抽象方法和非抽象方法,而接口中只能定义抽象方法。
  2. 接口的出现可以多实现。抽象类只能单继承。也就是说:接口的出现避免了单继承的局限性。
  3. 继承和实现的关系不一致。继承:is a,实现:like a

异常

异常:就是程序在运行时出现不正常情况。异常由来:问题也是现实生活中一个具体的事物,也可以通过java的类的形式进行描述。并封装成对象。其实就是java对不正常情况进行描述后的对象体现。

对于问题的划分:两种:一种是严重的问题,一种非严重的问题。

对于严重的,java通过Error类进行描述。对于Error一般不编写针对性的代码对其进行处理。

对与非严重的,java通过Exception类进行描述。对于Exception可以使用针对性的处理方式进行处理。

无论Error或者Exception都具有一些共性内容比如:不正常情况的信息,引发原因等。

Throwable |--Error |--Exception

异常的处理

java 提供了特有的语句进行处理。

try {
	需要被检测的代码;
} catch(异常类 变量) {
	处理异常的代码;(处理方式)
} finally {
	一定会执行的语句;
}

对捕获到的异常对象进行常见方法操作。String getMessage():获取异常信息。

在函数上声明异常。便于提高安全性,让调用出进行处理。不处理编译失败。

对多异常的处理。

  1. 声明异常时,建议声明更为具体的异常。这样处理的可以更具体。
  2. 对方声明几个异常,就对应有几个catch块。不要定义多余的catch块。如果多个catch块中的异常出现继承关系,父类异常catch块放在最下面。

建立在进行catch处理时,catch中一定要定义具体处理方式。不要简单定义一句 e.printStackTrace(),也不要简单的就书写一条输出语句。

因为项目中会出现特有的问题,而这些问题并未被java所描述并封装对象。所以对于这些特有的问题可以按照java的对问题封装的思想。将特有的问题。进行自定义的异常封装。

自定义异常

需求:在本程序中,对于除数是-1,也视为是错误的是无法进行运算的。那么就需要对这个问题进行自定义的描述。

当在函数内部出现了throw抛出异常对象,那么就必须要给对应的处理动作。 要么在内部try catch处理。 要么在函数上声明让调用者处理。

一般情况在,函数内出现异常,函数上需要声明。

发现打印的结果中只有异常的名称,却没有异常的信息。 因为自定义的异常并未定义信息。

如何定义异常信息呢? 因为父类中已经把异常信息的操作都完成了。 所以子类只要在构造时,将异常信息传递给父类通过super语句。 那么就可以直接通过getMessage方法获取自定义的异常信息。

自定义异常,必须是自定义类继承Exception。

继承Exception原因:异常体系有一个特点:因为异常类和异常对象都被抛出。 他们都具备可抛性。这个可抛性是Throwable这个体系中独有特点。只有这个体系中的类和对象才可以被throws和throw操作。

throws和throw的区别

  • throws使用在函数上。
  • throw使用在函数内。
  • throws后面跟的异常类。可以跟多个。用逗号隔开。
  • throw后跟的是异常对象。

Exceptoin中有一个特殊的子类异常RuntimeException 运行时异常。如果在函数内容抛出该异常,函数上可以不用声明,编译一样通过。如果在函数上声明了该异常。调用者可以不用进行处理。编译一样通过;

之所以不用在函数声明,是因为不需要让调用者处理。当该异常发生,希望程序停止。因为在运行时,出现了无法继续运算的情况,希望停止程序后,对代码进行修正。

自定义异常时:如果该异常的发生,无法在继续进行运算,就让自定义异常继承RuntimeException。

对于异常分两种:

  • 编译时被检测的异常。
  • 编译时不被检测的异常(运行时异常。RuntimeException以及其子类)

异常的好处

  1. 将问题进行封装。 2.将正常流程代码和问题处理代码相分离,方便于阅读。

异常的处理原则:

  1. 处理方式有两种:try 或者 throws。

  2. 调用到抛出异常的功能时,抛出几个,就处理几个。一个try对应多个catch。

  3. 多个catch,父类的catch放到最下面。

  4. catch内,需要定义针对性的处理方式。不要简单的定义printStackTrace,输出语句。也不要不写。当捕获到的异常,本功能处理不了时,可以继续在catch中抛出。

     try {
     	throw new AException();
     } catch (AException e) {
     	throw e;
     }
    

如果该异常处理不了,但并不属于该功能出现的异常。可以将异常转换后,在抛出和该功能相关的异常。

或者异常可以处理,当需要将异常产生的和本功能相关的问题提供出去,当调用者知道。并处理。也可以将捕获异常处理后,转换新的异常。

	try{
		throw new AException();
	} catch (AException e) {
		// 对AException处理。
		throw new BException();
	}

异常的注意事项,在子父类覆盖时:

  • 子类抛出的异常必须是父类的异常的子类或者子集。
  • 如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能try不能抛。

内部类

内部类的访问规则:

  1. 内部类可以直接访问外部类中的成员,包括私有。之所以可以直接访问外部类中的 成员,是因为内部类中持有了一个外部类的引用,格式 外部类名.this
  2. 外部类要访问内部类,必须建立内部类对象。

访问格式:

  1. 当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中。可以直接建立内部类对象。格式:

    外部类名.内部类名 变量名 = 外部类对象.内部类对象; Outer.Inner in = new Outer().new Inner();

  2. 当内部类在成员位置上,就可以被成员修饰符所修饰。比如,

    private:将内部类在外部类中进行封装。 static:内部类就具备static的特性。

    当内部类被static修饰后,只能直接访问外部类中的static成员。出现了访问局限。

    在外部其他类中,如何直接访问static内部类的非静态成员呢?

     new Outer.Inner().function();
    

    在外部其他类中,如何直接访问static内部类的静态成员呢?

     uter.Inner.function();
    

    注意:当内部类中定义了静态成员,该内部类必须是static的。当外部类中的静 态方法访问内部类时,内部类也必须是static的。

当描述事物时,事物的内部还有事物,该事物用内部类来描述。 因为内部事务在使用外部事物的内容。

内部类定义在局部时,

  1. 不可以被成员修饰符修饰
  2. 可以直接访问外部类中的成员,因为还持有外部类中的引用。但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。
  3. 匿名内部类的格式: new 父类或者接口(){定义子类的内容}
  4. 其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。可以理解为带内容的对象。
  5. 匿名内部类中定义的方法最好不要超过3个。

多态

  1. 表现:父类或者接口的引用指向了或者接收了自己的子类对象。

  2. 前提:

    • 类与类之间要有关系。继承,实现。
    • 通常都会有覆盖。
  3. 好处:预先定义的程序可以运行后期程序的内容。增强了程序的扩展性。

  4. 弊端:虽然可以预先使用,但是只能访问父类中已有的功能,运行的是后期子类的功能内容不能预先使用子类中定义的特有功能。

  5. 多态的注意事项:

在代码中。对于成员函数:Fu f = new Zi(); f.method();编译时期:看左边。运行时期:看右边。因为成员函数有一个覆盖操作。毕姥爷和毕老师的故事。

对于非私有的实例变量,静态变量,静态方法。编译和运行都看左边。

  1. 转型。子类对象被父类引用:子类对象在向上转型。将指向子类对象的父类应用转换成子类类型引用:向下转型。

毕姥爷和毕老师的故事。

class 毕姥爷 {}

class 毕老师 extends 毕姥爷 {}

毕姥爷 ly = new 毕老师(); //毕老师向上转型为了毕姥爷。向上转型

毕老师 ls = (毕老师)ly; //将代表毕老师对象的父类引用ly强制转换成了毕老师类型。向下转型。

多线程

进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。

线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。

一个进程中至少有一个线程。

Java VM 启动的时候会有一个进程java.exe.

该进程中至少一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。

扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。

创建线程的第一种方式:继承Thread类。步骤:

  1. 定义类继承Thread。
  2. 复写Thread类中的run方法。目的:将自定义代码存储在run方法。让线程运行。
  3. 调用线程的start方法,该方法两个作用:启动线程,调用run方法。

发现运行结果每一次都不同。因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。

这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。

为什么要覆盖run方法呢?

Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。也就是说Thread类中的run方法,用于存储线程要运行的代码。

创建线程的第二种方式:实现Runable接口,步骤:

  1. 定义类实现Runnable接口

  2. 覆盖Runnable接口中的run方法。 将线程要运行的代码存放在该run方法中。

  3. 通过Thread类建立线程对象。

  4. 将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

    为什么要将Runnable接口的子类对象传递给Thread的构造函数。 因为,自定义的run方法所属的对象是Runnable接口的子类对象。 所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。

  5. 调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

实现方式和继承方式有什么区别呢?

实现方式好处:避免了单继承的局限性。在定义线程时,建立使用实现方式。

两种方式区别:

  • 继承Thread:线程代码存放Thread子类run方法中。
  • 实现Runnable,线程代码存在接口的子类的run方法。

买票的例子,通过分析,发现,打印出0,-1,-2等错票。

多线程的运行出现了安全问题。

问题的原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。

解决办法:对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

Java对于多线程的安全问题提供了专业的解决方式。就是同步代码块。

synchronized(对象) {
    需要被同步的代码
}

对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

火车上的卫生间---经典。

同步的前提:

  1. 必须要有两个或者两个以上的线程。
  2. 必须是多个线程使用同一个锁。

必须保证同步中只能有一个线程在运行。

好处:解决了多线程的安全问题。

弊端:多个线程需要判断锁,较为消耗资源,

join:当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。join可以用来临时加入线程执行。

如何停止线程?stop方法已经过时。

只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。

只要控制住循环,就可以让run方法结束,也就是线程结束。

特殊情况:当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。

当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。 强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。

Thread类提供该方法 interrupt();

线程间通信。等待/唤醒机制。也就是常见的生产者消费者问题。

  1. 当多个生产者消费者出现时,需要让获取执行权的线程判断标记。通过while完成
  2. 需要将对方的线程唤醒。仅仅用notify,是不可以的。因为有可能出现只唤醒本方。有可能会导致,所有线程都等待。所以可以通过notifyAll的形式来完成 。

这个程序有一个bug。就是每次notifyAll。都会唤醒本方。可不可以只唤醒对方呢?

JDK1.5版本提供了一些新的对象,优化了等待唤醒机制。

  1. 将synchronized 替换成了Lock接口。

    将隐式锁,升级成了显示锁。Lock

    • 获取锁:lock();
    • 释放锁:unlock();注意:释放的动作一定要执行,所以通常定义在finally中。
    • 获取Condition对象:newCondition();
  2. 将Object中的wait,notify,notifyAll方法都替换成了Condition的await,signal,signalAll。和以前不同是:一个同步代码块具备一个锁,该所以具备自己的独立wait和notify方法。现在是将wait,notify等方法,封装进一个特有的对象Condition,而一个Lock锁上可以有多个Condition对象。

     Lock lock = new ReentrantLock();
     Condition conA = lock.newCondition();
     Condition conB = lock.newCondition();
     con.await();//生产,,消费
     con.signal();生产
     set() {
     	if(flag)
     		conA.await();//生产者,
     	code......;
    
     	flag = true;
     	conB.signal();
     }
     
     out() {
     	if(!flag)
     		conB.await();//消费者
     	code....;
     	flag = false;
     	conA.signal();
     }
    

wait和sleep的区别:

  • wait:释放cpu执行权,释放同步中锁。
  • sleep:释放cpu执行权,不释放同步中锁。

synchronized(锁) { wait(); }

停止线程,stop过时。原理:run方法结束。run方法中通常定义循环,指定控制住循环线程即可结束。

  1. 定义结束标记。
  2. 当线程处于了冻结状态,没有执行标记,程序一样无法结束。这时可以循环,正常退出冻结状态,或者强制结束冻结状态。强制结束冻结状态:interrupt();目的是线程强制从冻结状态恢复到运行状态。但是会发生InterruptedException异常。

线程中一些常见方法:

  • setDaemon(boolean):将线程标记为后台线程,后台线程和前台线程一样,开启,一样抢执行权运行,只有在结束时,有区别,当前台线程都运行结束后,后台线程会自动结束。
  • join():什么意思?等待该线程结束。当A线程执行到了B的.join方法时,A就会处于冻结状态。A什么时候运行呢?当B运行结束后,A就会具备运行资格,继续运行。加入线程,可以完成对某个线程的临时加入执行。

基本数据类型

基本数据类型 对象
byte Byte
short short
int Integer
long Long
boolean Boolean
float Float
double Double
char Character

基本数据类型对象包装类的最常见作用,就是用于基本数据类型和字符串类型之间做转换

基本数据类型转成字符串。

  • 基本数据类型+""
  • 基本数据类型.toString(基本数据类型值);如: Integer.toString(34);//将34整数变成"34";

字符串转成基本数据类型。

xxx a = Xxx.parseXxx(String);
int a = Integer.parseInt("123");
double b = Double.parseDouble("12.23");
boolean b = Boolean.parseBoolean("true");
Integer i = new Integer("123");
int num = i.intValue();

十进制转成其他进制。

  • toBinaryString();
  • toHexString();
  • toOctalString();

其他进制转成十进制

  • parseInt(string,radix);

StringBuffer字符串缓冲区

是一个容器。特点:

  • 长度是可变化的。
  • 可以字节操作多个数据类型。
  • 最终会通过toString方法变成字符串。

C create U update R read D delete

  • 存储。

      StringBuffer append():将指定数据作为参数添加到已有数据结尾处。
      StringBuffer insert(index,数据):可以将数据插入到指定index位置。
    
  • 删除

      StringBuffer delete(start,end):删除缓冲区中的数据,包含start,不包含end。
      StringBuffer deleteCharAt(index):删除指定位置的字符。
    
  • 获取

      char charAt(int index) 
      int indexOf(String str) 
      int lastIndexOf(String str) 
      int length() 
      String substring(int start, int end) 
    
  • 修改。

      StringBuffer replace(start,end,string);
      void setCharAt(int index, char ch) ;
    
  • 反转。

      StringBuffer reverse();
    
  • 将缓冲区中指定数据存储到指定字符数组中。

      void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 
    

JDK1.5 版本之后出现了StringBuilder.

  • StringBuffer是线程同步。
  • StringBuilder是线程不同步。

以后开发,建议使用StringBuilder

升级三个因素:

  1. 提高效率。
  2. 简化书写。
  3. 提高安全性。