java各知识点详细总结 (1)

时间:2024.4.27

Java知识

第1章

一、java概述

1、java发展:

1991 年Sun公司的James Gosling等人开始开发名称为 Oak 的语言,希望用于控制嵌入在有线电视交换盒、PDA等的微处理器;

1994年将Oak语言更名为Java;

2、Java的三种技术架构:

JAVAEEJava Platform Enterprise Edition,开发企业环境下的应用程序,主要针对web程序开发;

JAVASEJava Platform Standard Edition,完成桌面应用程序的开发,是其它两者的基础;

JAVAMEJava Platform Micro Edition,开发电子消费产品和嵌入式设备,如手机中的程序;

3、JDK:Java Development Kitjava的开发和运行环境,java的开发工具集和jre。

4、JRE:Java Runtime Environmentjava程序的运行环境,java运行的所需的类库+JVM(java虚拟机)。

5、配置环境变量:

6、javac命令和java命令

    要知道java是分两部分的:一个是编译,一个是运行。

    javac负责的是编译的部分,当执行javac时,会启动java的编译器程序。对指定扩展名的.java文件进行编译,生成了jvm可以识别的字节码文件,也就是class文件,也就是java的运行程序。

    java负责运行的部分.会启动jvm.加载运行时所需的类库,并对class文件进行执行。

    一个文件要被执行,必须要有一个执行的起始点,这个起始点就是main方法.

7、虚拟机

   当我们在虚拟机中进行软件评测时,可能系统一样会崩溃,但是,崩溃的只是虚拟机上的操作系统,而不是物理计算机上的操作系统,并且,使用虚拟机的“Undo”(恢复)功能,可以马上恢复虚拟机到安装软件之前的状态。

8、public class和class

● public class:文件名称和类名称一致,一个*.java文件之中只能够存在一个public class定义。

● class:文件名称和类名称不一致,一个*.java文件之中可以同时存在多个class定义,并且编译之后会形成不同的*.class文件。

第2章   java语法基础

1、关键字:某种语言赋予了特殊含义的单词。

保留字:还没有赋予特殊含义,但是准备日后要使用的单词。

2、标示符:就是在程序中自定义的名词。比如类名,变量名,方法名。包含 0-9、a-z、$、_ ;

    注意: 1)数字不可以开头。 2)不可以使用关键字。

3、常量:是在程序中不会变化的数据。

4、变量:其实就是内存中的一个存储空间,用于存储数据。

    作用:方便运算。因为有些数据不确定,所以确定该数据的名字和存储空间。

    特点:变量空间可以重复使用。

什么时候定义变量?只要是数据不确定的时候,就定义变量。

变量空间的开辟需要什么要素呢?

    (1)数据类型。 (2)变量名称。  (3)变量的初始化值。

变量的作用域和生存期:

变量的作用域:

    作用域从变量定义的位置开始,到该变量所在的那对大括号结束;

生命周期:

    变量从定义的位置开始就在内存中有生命了;

    变量到达它所在的作用域的时候就在内存中消失了;

5、数据类型

    1)基本数据类型:byte、short、int、long、float、double、char、boolean

    2)引用数据类型: 数组、类、接口。

级别从低到高为:byte,char,short(这三个平级)-->int-->float-->long-->double

自动类型转换:从低级别到高级别,系统自动转换;

强制类型转换:什么情况下使用?把一个高级别的数赋给一个别该数的级别低的变量;

6、运算符号:

    1)算术运算符。

       + - * / %   %:任何整数模2不是0就是1,所以只要改变被模数就可以实现开关运算。

       +:连接符。

       ++,--

    2)赋值运算符。

       =  += -= *= /= %=

    3)比较运算符。

       特点:该运算符的特点是:运算完的结果,要么是true,要么是false。

    4)逻辑运算符。

       &  |  ^  !   &&   ||

       逻辑运算符除了 !  外都是用于连接两个boolean类型表达式。

       &: 只有两边都为true结果是true。否则就是false。

       |:只要两边都为false结果是false,否则就是true

       ^:异或: 两边结果一样,就为false。两边结果不一样,就为true.

       & 和 &&区别: & :无论左边结果是什么,右边都参与运算。

                     &&:短路与,如果左边为false,那么右边不参数与运算。

       | 和|| 区别:|:两边都运算。

                     ||:短路或,如果左边为true,那么右边不参与运算。

    5)位运算符:用于操作二进制位的运算符。

       &  |  ^

       <<  >>   >>>(无符号右移)

    7、语句

     If  switch  do while   while  for

   1)当判断固定个数的值的时候,可以使用if,也可以使用switch。但是建议使用switch,效率相对较高。

switch(变量){

   case 值:要执行的语句;break;

   …

   default:要执行的语句;

}

 工作原理:用小括号中的变量的值依次和case后面的值进行对比,和哪个case后面的值相同了

           就执行哪个case后面的语句,如果没有相同的则执行default后面的语句;

 细节:(1)break是可以省略的,如果省略了就一直执行到遇到break为止;

        (2)switch 后面的小括号中的变量应该是byte,char,short,int四种类型中的一种;

        (3)default可以写在switch结构中的任意位置;如果将default语句放在了第一行,则

不管switch中的变量与case中的值是否匹配,程序会从default开始执行直到第一个

break出现。

    2)当判断数据范围,获取判断运算结果boolean类型时,需要使用if。

    3)当某些语句需要执行很多次时,就用循环结构。

    while和for可以进行互换。

    区别在于:如果需要定义变量控制循环次数。建议使用for。因为for循环完毕,变量在内存中释放。

break:作用于switch ,和循环语句,用于跳出,或者称为结束。

break语句单独存在时,下面不要定义其他语句,因为执行不到,编译会失败。当循环嵌套时,break只跳出当前所在循环。要跳出嵌套中的外部循环,只要给循环起名字即可,这个名字称之为标号

continue:只作用于循环结构,继续循环用的。

作用:结束本次循环,继续下次循环。该语句单独存在时,下面不可以定义语句,执行不到。

8、方法为了提高代码的复用性,可以将其定义成一个单独的功能,该功能的体现就是java中的方法。

java中的方法的定义格式:

    修饰符 返回值类型 方法名(参数类型 形式参数1,参数类型 形式参数1,…){

      执行语句;

      return 返回值;

   }

当方法没有具体的返回值时,返回的返回值类型用void关键字表示。

如果方法的返回值类型是void时,return语句可以省略不写的,系统会帮你自动加上。

return的作用:结束方法。结束功能。

如何定义一个方法?

    方法其实就是一个功能,定义方法就是实现功能,通过两个明确来完成:

    1)明确该功能的运算完的结果,其实是在明确这个方法的返回值类型。

    2)在实现该功能的过程中是否有未知内容参与了运算,其实就是在明确这个方法的参数列表(参数类型&参数个数)。

方法的作用:

1)用于定义功能。

2)用于封装代码提高代码的复用性。

注意:方法中只能调用方法,不能定义方法。

主方法:

    1)保证该类的独立运行。

    2)因为它是程序的入口。

    3)它被jvm调用。

重载的定义是:在一个类中,如果出现了两个或者两个以上的同名方法,只要它们的参数的个数,或者参数的类型不同,即可称之为该方法重载了。

如何区分重载:当方法同名时,只看参数列表。和返回值类型没关系。

9、数 组:用于存储同一类型数据的一个容器。好处:可以对该容器中的数据进行编号,从0开始。数组用于封装数据,就是一个具体的实体。

如何在java中表现一个数组呢?两种表现形式。

1)元素类型[] 变量名 = new 元素类型[元素的个数];

2)元素类型[] 变量名 = {元素1,元素2...};

元素类型[] 变量名 = new 元素类型[]{元素1,元素2...};

第3章   面向对象

一、在类中的定义都称之为成员。成员有两种:

1、成员属性:对应的就是事物的属性。

2、成员方法:对应的就是事物的行为。

必须先要对事物进行属性和行为的分析,才可以用代码来体现。

注意:主方法的存在,仅为该类是否需要独立运行,如果不需要,主方法是不用定义的。

主方法的解释:保证所在类的独立运行,是程序的入口,被jvm调用。

成员属性和局部变量的区别:

1、成员属性直接定义在类中。

   局部变量定义在方法中,参数上,语句中。

2、成员属性在这个类中有效。

局部变量只在自己所属的大括号内有效,大括号结束,局部变量失去作用域。

3、成员属性存在于堆内存中,随着对象的产生而存在,消失而消失。

局部变量存在于栈内存中,随着所属区域的运行而存在,结束而释放。

二、构造方法:用于给对象进行初始化,是给与之对应的对象进行初始化,它具有针对性,方法中的一种。

特点

1该方法的名称和所在类的名称相同。

2不需要定义返回值类型。

3该方法没有具体的返回值。

记住:所有对象创建时,都需要初始化才可以使用。

注意事项:一个类在定义时,如果没有定义过构造方法,那么该类中会自动生成一个空参数的构造方法,为了方便该类创建对象,完成初始化。如果在类中自定义了构造方法,那么默认的构造方法就没有了。

一个类中,可以有多个构造方法,因为它们的方法名称都相同,所以只能通过参数列表来区分。所以,一个类中如果出现多个构造方法。它们的存在是以重载体现的。

构造方法和一般方法有什么区别呢?

1两个方法定义格式不同。

2构造方法是在对象创建时,就被调用,用于初始化,而且初始化动作只执行一次。

一般方法,是对象创建后,需要调用才执行,可以被调用多次。

  

三、封 装(面向对象特征之一):是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

好处:将变化隔离;便于使用;提高重用性;安全性。

封装原则:将不需要对外提供的内容都隐藏起来,把属性都隐藏,提供公共方法对其访问。

注意:私有仅仅是封装的一种体现形式而已。

private int age; //私有的访问权限最低,只有在本类中的访问有效。

私有的成员:其他类不能直接创建对象访问,所以只有通过本类对外提供具体的访问方式来完成对私有的访问,可以通过对外提供方法的形式对其进行访问。

好处:可以在方法中加入逻辑判断等操作,对数据进行判断等操作。

总结:属性是用于存储数据的,直接被访问,容易出现安全隐患,所以,类中的属性通常被私有化,并对外提供公共的访问方法。

这个方法一般有两个,规范写法:对于属性,可以使用setter(),getter()方法对其进行操作。

四、this:代表对象。就是所在方法所属对象的引用。哪个对象调用了this所在的方法,this就代表哪个对象,就是哪个对象的引用。

在定义功能时,如果该功能内部使用到了调用该功能的对象,这时就用this来表示这个对象。

this 还可以用于构造方法间的调用。

调用格式:this(实际参数);

this对象后面跟上 .  调用的是成员属性和成员方法(一般方法);

this对象后面跟上 () 调用的是本类中的对应参数的构造方法。

注意:用this调用构造方法,必须定义在构造方法的第一行。因为构造方法是用于初始化的,所以初始化动作一定要执行。否则编译失败。

五、static: 关键字,是一个修饰符,用于修饰成员(成员属性和成员方法)。

特点:

1、想要实现对象中的共性数据共享。可以将这个数据进行静态修饰。

2、被静态修饰的成员,可以直接被类名所调用。也就是说,静态的成员多了一种调用方式。类名.静态方式

3、静态随着类的加载而加载。而且优先于对象存在。

弊端:

1、有些数据是对象特有的数据,是不可以被静态修饰的。因为那样的话,特有数据会变成对象的共享数据。这样对事物的描述就出了问题。所以,在定义静态时,必须要明确,这个数据是否是被对象所共享的。

2、静态方法只能访问静态成员,不可以访问非静态成员。

(这句话是针对同一个类环境下的,比如说,一个类有多个成员(属性,方法,字段),静态方法 那么可以访问同类名下其他静态成员,如果访问非静态成员就不行),因为静态方法加载时,优先于对象存在,所以没有办法访问对象中的成员。

3、静态方法中不能使用this,super关键字。

因为this代表对象,而在静态时,有可能没有对象,所以this无法使用。

4、主方法是静态的。

成员属性和静态变量的区别:

1,成员属性所属于对象。所以也称为实例变量。

静态变量所属于类。所以也称为类变量。

2,成员属性存在于堆内存中。

静态变量存在于方法区中。

3,成员属性随着对象创建而存在。随着对象被回收而消失。

静态变量随着类的加载而存在。随着类的消失而消失。

4,成员属性只能被对象所调用 。

静态变量可以被对象调用,也可以被类名调用。

所以,成员属性可以称为对象的特有数据,静态变量称为对象的共享数据。

匿名对象使用场景

1当对方法只进行一次调用的时候,可以使用匿名对象。

2当对象对成员进行多次调用时,不能使用匿名对象。必须给对象起名字。

-------------------------------------------------------------------------------------------------

六、继 承(面向对象特征之一)

好处:

1、提高了代码的复用性。

2、让类与类之间产生了关系,提供了另一个特征多态的前提。

父类:其实是由多个类不断向上抽取共性内容而来的。java只支持单继承。java虽然不直接支持多继承,但是保留了这种多继承机制,进行改良。

单继承:一个类只能有一个父类。

多继承:一个类可以有多个父类。

为什么不支持多继承呢?

因为当一个类同时继承两个父类时,两个父类中有相同的功能,那么子类对象调用该功能时,运行哪一个呢?因为父类中的方法中存在方法体。

但是java支持多重继承。A继承B  B继承C  C继承D。

多重继承的出现,就有了继承体系。体系中的顶层父类是通过不断向上抽取而来的。父类里面定义的该体系最基本最共性内容的功能。

所以,一个体系要想被使用,直接查阅该系统中的父类的功能即可知道该体系的基本用法。那么想要使用一个体系时,需要建立对象。建议建立最子类对象,因为最子类不仅可以使用父类中的功能。还可以使用子类特有的一些功能。

简单说:对于一个继承体系的使用,查阅顶层父类中的内容,创建最底层子类的对象。

子父类出现后,类中的成员都有了哪些特点:

1、成员属性。

     当子父类中出现一样的属性时,子类类型的对象,调用该属性,值是子类的属性值。

     如果想要调用父类中的属性值,需要使用一个关键字:super

     This代表是本类类型的对象引用。

    Super代表是子类所属的父类中的内存空间引用。

     注意:子父类中通常是不会出现同名成员属性的,因为父类中只要定义了,子类就不用在定义了,直接继承过来用就可以了。

2、成员方法。

当子父类中出现了一模一样的方法时,建立子类对象会运行子类中的方法。好像父类中的方法被覆盖掉一样。所以这种情况,是方法的另一个特性:覆盖(复写,重写)

什么时候使用覆盖呢?当一个类的功能内容需要修改时,可以通过覆盖来实现。

3、构造方法。

发现子类构造方法运行时,先运行了父类的构造方法。为什么呢?

原因:子类的所有构造方法中的第一行,其实都有一条隐身的语句super();

super(): 表示父类的构造方法,并会调用于参数相对应的父类中的构造方法。而super()是在调用父类中空参数的构造方法。

为什么子类对象初始化时,都需要调用父类中的方法?(为什么要在子类构造方法的第一行加入这个super()?)

因为子类继承父类,会继承到父类中的数据,所以必须要看父类是如何对自己的数据进行初始化的。所以子类在进行对象初始化时,先调用父类的构造方法,这就是子类的实例化过程

注意:子类中所有的构造方法都会默认访问父类中的空参数的构造方法,因为每一个子类构造内第一行都有默认的语句super();

如果父类中没有空参数的构造方法,那么子类的构造方法内,必须通过super语句指定要访问的父类中的构造方法。

如果子类构造方法中用this来指定调用子类自己的构造方法,那么被调用的构造方法也一样会访问父类中的构造方法。

问题:super()和this()是否可以同时出现的构造方法中?

两个语句只能有一个定义在第一行,所以只能出现其中一个。

super()或者this():为什么一定要定义在第一行?

因为super()或者this()都是调用构造方法,构造方法用于初始化,所以初始化的动作要先完成。

继承的细节:

什么时候使用继承呢?

当类与类之间存在着所属关系时,才具备了继承的前提。a是b中的一种。a继承b。如:狼是犬科中的一种。

注意:不要仅仅为了获取其他类中的已有成员进行继承。

所以判断所属关系,可以简单看,如果继承后,被继承的类中的功能,都可以被该子类所具备,那么继承成立。如果不是,不可以继承。

在方法覆盖时,注意两点:

1、子类覆盖父类时,必须要保证,子类方法的权限必须大于等于父类方法权限可以实现继承。否则,编译失败。

2、覆盖时,要么都静态,要么都不静态。 (静态只能覆盖静态,或者被静态覆盖)

继承的一个弊端:打破了封装性。对于一些类,或者类中功能,是需要被继承,或者覆写的。

七、final特点:

1:这个关键字是一个修饰符,可以修饰类,方法,变量。

2:被final修饰的类是一个最终类,不可以被继承。

3:被final修饰的方法是一个最终方法,不可以被覆盖。

4:被final修饰的变量是一个常量,只能赋值一次。

    不加final修饰不是也可以使用吗?那么这个值是一个变量,是可以更改的。加了final,程序更为严谨。常量名称定义时,有规范,所有字母都大写,如果由多个单词组成,中间用 _ 连接。

八、抽象类: abstract

抽象:不具体,看不明白。抽象类表象体现。

在不断抽取过程中,将共性内容中的方法声明抽取,但是方法不一样,没有抽取,这时抽取到的方法,并不具体,需要被指定关键字abstract所标示,声明为抽象方法。

抽象方法所在类一定要标示为抽象类,也就是说该类需要被abstract关键字所修饰。

抽象类的特点:

1、抽象方法只能定义在抽象类中,抽象类和抽象方法必须由abstract关键字修饰(可以描述类和方法,不可以描述变量)。

2、抽象方法只定义方法声明,并不定义方法实现。

3、抽象类不可以被创建对象(实例化)。

4、只有通过子类继承抽象类并覆盖了抽象类中的所有抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。

抽象类的细节:

1、抽象类中是否有构造方法?有,用于给子类对象进行初始化。

2、抽象类中是否可以定义非抽象方法?

      可以。其实,抽象类和一般类没有太大的区别,都是在描述事物,只不过抽象类在描述事物时,有些功能不具体。所以抽象类和一般类在定义上,都是需要定义属性和行为的。只不过,比一般类多了一个抽象方法。而且比一般类少了一个创建对象的部分。

3、抽象关键字abstract和哪些不可以共存?final , private , static

4、抽象类中可不可以不定义抽象方法?可以。抽象方法目的仅仅为了不让该类创建对象。

-------------------------------------------------------------------------------------

九、接 口

1、是用关键字interface定义的。

2、接口中包含的成员,最常见的有全局常量、抽象方法。

注意:接口中的成员都有固定的修饰符。

    成员属性:public static final

   成员方法:public abstract

interface Inter{

    public static final int x = 3;

    public abstract void show();

}

3、接口中有抽象方法,说明接口不可以实例化接口的子类必须实现了接口中所有的抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。

4、类与类之间存在着继承关系,类与接口中间存在的是实现关系。即继承用extends  ;实现用implements ;

5、接口和类不一样的地方是接口可以被多实现,这就是多继承改良后的结果。java将多继承机制通过多实现来体现。

6、一个类在继承另一个类的同时,还可以实现多个接口。所以接口的出现避免了单继承的局限性。还可以将类进行功能的扩展。

7、其实java中是有多继承的。接口与接口之间存在着继承关系,接口可以多继承接口

接口都用于设计上,设计上的特点:(可以理解主板上提供的接口)

1:接口是对外提供的规则。

2:接口是功能的扩展。

3:接口的出现降低了耦合性。

抽象类与接口:

抽象类:一般用于描述一个体系单元,将一组共性内容进行抽取。特点:可以在类中定义抽象内容让子类实现,可以定义非抽象内容让子类直接使用。它里面定义的都是一些体系中的基本内容

接口:一般用于定义对象的扩展功能,是在继承之外还需这个对象具备的一些功能。

抽象类和接口的共性:都是不断向上抽取的结果。

抽象类和接口的区别:

1、抽象类只能被继承,而且只能单继承。接口需要被实现,而且可以多实现。

2、抽象类中可以定义非抽象方法,子类可以直接继承使用。接口中都是抽象方法,需要子类去实现。

3、抽象类的成员修饰符可以自定义。接口中的成员修饰符是固定的。全都是public的。

------------------------------------------------------------------------------------------------

十、多 态(面向对象特征之一):方法本身就具备多态性,某一种事物有不同的具体的体现。

体现:父类引用或者接口的引用指向了自己的子类对象。//Animal a = new Cat();父类可以调用子类中覆写过的(父类中有的方法)

多态的好处:提高了程序的扩展性。继承的父类或接口一般是类库中的东西,(如果要修改某个方法的具体实现方式)只有通过子类去覆写要改变的某一个方法,这样在通过将父类的应用指向子类的实例去调用覆写过的方法就可以。

多态的弊端:当父类引用指向子类对象时,虽然提高了扩展性,但是只能访问父类中具备的方法,不可以访问子类中特有的方法。(前期不能使用后期产生的功能,即访问的局限性)

多态的前提:

    1、必须要有关系,比如继承、或者实现。

   2、通常会有覆盖操作。

多态的出现思想上也做着变化:以前是创建对象并指挥对象做事情。有了多态以后,我们可以找到对象的共性类型,直接操作共性类型做事情即可,这样可以指挥一批对象做事情,即通过操作父类或接口实现。

--------------------------------------------------------------

class A1{

    void 讲课(){

       System.out.println("企业管理");

    }

    void 钓鱼(){

       System.out.println("钓鱼");

    }

}

class A11 extends A1{

    void 讲课(){

       System.out.println("JAVA");

    }

    void 看电影(){

       System.out.println("看电影");

    }

}

class {

    public static void main(String[] args) {

       A1 x = new A11();

//     x.讲课();

//     x.看电影();  //错误.

       A11 y = (A11)x;

       y.看电影();//在多态中,自始自终都是子类对象在做着类型的变化。

    }

}

---------------------------------------------------------------

如果想用子类对象的特有方法,如何判断对象是哪个具体的子类类型呢?

可以通过一个关键字 instanceof ;//判断对象是否实现了指定的接口或继承了指定的类

格式:<对象 instanceof 类型> ,判断一个对象是否所属于指定的类型。

Student instanceof Person = true;//student继承了person类

---------------------------------------------------------------

十一、包:定义包用package关键字。

1、给类文件提供多层名称空间。

如果生成的包不在当前目录下,需要最好执行classpath,将包所在父目录定义到classpath变量中即可。

类的全名称是 包名.类名

    编译命令:javac –d 位置(.当前路径) java源文件 (就可以自动生成包)

包是一种封装形式,用于封装类,想要被包以外的程序访问,该类必须为public;

类中的成员,如果被包以外访问,也必须public;

包与包之间访问可以使用的权限有两种:

1、public

2、protected:只能是不同包中的子类可以使用的权限。

总结java中的四种权限:

    范围       public     protected   default     private

同一个类中       ok             ok            ok         ok

同一包中         ok             ok            ok    

子类             ok        

不同包中         ok

-----------------------------------------------------------------------------------------------

Import - 导入:类名称变长,写起来很麻烦。为了简化,使用了一个关键字:import,可以使用这个关键字导入指定包中的类。记住:实际开发时,到的哪个类就导入哪个类,不建议使用*.

import packa.*;//这个仅仅是导入了packa当前目录下的所有的类。不包含子包。

import packa.abc.*;//导入了packa包中的子包abc下的当前的所有类。

如果导入的两个包中存在着相同名称的类。这时如果用到该类,必须在代码中指定包名。

十二、常见的软件包:

java.lang : language java的核心包,Object System  String Throwable jdk1.2版本后,该包中的类自动被导入。

java.awt : 定义的都是用于java图形界面开发的对象。

javax.swing:提供所有的windows桌面应用程序包括的控件,比如:Frame , Dialog, Table, List 等等,就是java的图形界面库。

java.net : 用于java网络编程方面的对象都在该包中。

java.io : input  output 用于操作设备上数据的对象都在该包中。比如:读取硬盘数据,往硬盘写入数据。

java.util : java的工具包,时间对象,集合框架。

java.applet: application+let 客户端java小程序。server+let  -->  servlet 服务端java小程序。

jar java的压缩包,主要用于存储类文件,或者配置文件等。

命令格式:jar –cf 包名.jar 包目录

   解压缩:jar –xvf 包名.jar

 将jar包目录列表重定向到一个文件中:jar –tf 包名.jar >c:\1.txt

-----------------------------------------------------------------------------------------------

第4章

一、异 常:★★★★

异常:就是不正常。程序在运行时出现的不正常情况。其实就是程序中出现的问题。这个问题按照面向对象思想进行描述,并封装成了对象。因为问题的产生有产生的原因、有问题的名称、有问题的描述等多个属性信息存在。当出现多属性信息最方便的方式就是将这些信息进行封装。异常就是java按照面向对象的思想将问题进行对象封装。这样就方便于操作问题以及处理问题。

出现的问题有很多种,比如角标越界,空指针等都是。就对这些问题进行分类。而且这些问题都有共性内容比如:每一个问题都有名称,同时还有问题描述的信息,问题出现的位置,所以可以不断的向上抽取。形成了异常体系

--------java.lang.Throwable

Throwable可抛出的。

    |--Error错误,一般情况下,不编写针对性的代码进行处理,通常是jvm发生的,需要对程序进行修正。

    |--Exception异常,可以有针对性的处理方式。

无论是错误还是异常,它们都有具体的子类体现每一个问题,它们的子类都有一个共性,就是都以父类名作为子类的后缀名

这个体系中的所有类和对象都具备一个独有的特点;就是可抛性。

可抛性的体现:就是这个体系中的类和对象都可以被throws和throw两个关键字所操作。

------------------------------------------------------

class  ExceptionDemo{

    public static void main(String[] args) {

//     byte[] buf = new byte[1024*1024*700];//java.lang.OutOfMemoryError内存溢出错误

    }

}

------------------------------------------------------

在开发时,如果定义功能时,发现该功能会出现一些问题,应该将问题在定义功能时标示出来,这样调用者就可以在使用这个功能的时候,预先给出处理方式。

如何标示呢?通过throws关键字完成,格式:throws 异常类名,异常类名...

这样标示后,调用者,在使用该功能时,就必须要处理,否则编译失败。

处理方式有两种:1、捕捉;2、抛出。

对于捕捉:java有针对性的语句块进行处理。

try {

   需要被检测的代码;

}

catch(异常类 变量名){

   异常处理代码;

}

fianlly{

   一定会执行的代码;

}

--------------------------------------------------------

catch (Exception e) { //e用于接收try检测到的异常对象。

    System.out.println("message:"+e.getMessage());//获取的是异常的信息。

    System.out.println("toString:"+e.toString());//获取的是异常的名字+异常的信息。

    e.printStackTrace();//打印异常在堆栈中信息;异常名称+异常信息+异常的位置。

}

---------------------------------------------------------

异常处理原则:功能抛出几个异常,功能调用如果进行try处理,需要与之对应的catch处理代码块,这样的处理有针对性,抛几个就处理几个。

特殊情况:try对应多个catch时,如果有父类的catch语句块,一定要放在下面。

throw 和throws关键字的区别:

throw用于抛出异常对象,后面跟的是异常对象;throw用在方法

throws用于抛出异常类,后面跟的异常类名,可以跟多个,用逗号隔开。throws用在方法

通常情况:方法内容如果有throw,抛出异常对象,并没有进行处理,那么方法上一定要声明,否则编译失败。但是也有特殊情况。

异常分两种:

1、编译时被检查的异常,只要是Exception及其子类都是编译时被检测的异常。

2、运行时异常,其中Exception有一个特殊的子类RuntimeException,以及RuntimeException的子类是运行异常,也就说这个异常是编译时不被检查的异常。

编译时被检查的异常和运行时异常的区别:

编译被检查的异常在方法内被抛出,方法必须要声明,否则编译失败。

声明的原因:是需要调用者对该异常进行处理。

运行时异常如果在方法内被抛出,在方法上不需要声明。不声明的原因:不需要调用者处理,运行时异常发生,已经无法再让程序继续运行,所以,不让调用处理的,直接让程序停止,由调用者对代码进行修正。

定义异常处理时,什么时候定义try,什么时候定义throws呢?

功能内部如果出现异常,如果内部可以处理,就用try;

如果功能内部处理不了,就必须声明出来,让调用者处理,使用throws抛出,交给调用者处理。谁调用了这个功能谁就是调用者;

自定义异常:当开发时,项目中出现了java中没有定义过的问题时,这时就需要开发者按照java异常建立思想,将项目中的特有问题也进行对象的封装。这个异常,称为自定义异常。

对于除法运算,0作为除数是不可以的。java中对这种问题用ArithmeticException类进行描述。对于这个功能,在我们项目中,除数除了不可以为0外,还不可以为负数。可是负数的部分java并没有针对描述。所以我们就需要自定义这个异常。

自定义异常的步骤:

1、定义一个子类继承Exception或RuntimeException,让该类具备可抛性(既可以使用throw和throws去调用此类)。

2、通过throw 或者throws进行操作。

异常的转换思想:当出现的异常是调用者处理不了的,就需要将此异常转换为一个调用者可以处理的异常抛出。

try  catch  finally的几种结合方式:

1,

try

catch

finally

这种情况,如果出现异常,并不处理,但是资源一定关闭,所以try  finally集合只为关闭资源

记住:finally很有用,主要为用户关闭资源。无论是否发生异常,资源都必须进行关闭。

System.exit(0); //退出jvm,只有这种情况finally不执行。

当异常出现后,在子父类进行覆盖时,有了一些新的特点:

1、当子类覆盖父类的方法时,如果父类的方法抛出了异常,那么子类的方法要么不抛出异常要么抛出父类异常或者该异常的子类,不能抛出其他异常。

2、如果父类抛出了多个异常,那么子类在覆盖时只能抛出父类的异常的子集。

注意:

如果父类或者接口中的方法没有抛出过异常,那么子类是不可以抛出异常的,如果子类的覆盖的方法中出现了异常,只能try不能throws。

如果这个异常子类无法处理,已经影响了子类方法的具体运算,这时可以在子类方法中,通过throw抛出RuntimeException异常或者其子类,这样,子类的方法上是不需要throws声明的。

常见异常:

1、脚标越界异常(IndexOutOfBoundsException)包括数组、字符串;

空指针异常(NullPointerException)

2、类型转换异常:ClassCastException

3、没有这个元素异常:NullPointerException

4、不支持操作异常;

异常要尽量避免,如果避免不了,需要预先给出处理方式。

-----------------------------------------------------------------------------------------------

第5章

API(Application Programming Interface,应用程序编程接口)是一些预先定义的方法,目的是提供应用程序与开发人员基于某软件或硬件的以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

------java.lang.Object

Object所有类的直接或者间接父类,Java认为所有的对象都具备一些基本的共性内容,这些内容可以不断的向上抽取,最终就抽取到了一个最顶层的类中的,该类中定义的就是所有对象都具备的功能。

具体方法:

1、boolean equals(Object obj):用于比较两个对象是否相等,其实内部比较的就是两个对象地址。如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果;

而根据对象的属性不同,判断对象是否相同的具体内容也不一样。所以在定义类时,一般都会复写equals方法,建立本类特有的判断对象是否相同的依据。

  public boolean equals(Object obj){

     if(!(obj instanceof Person))

         return false;

     Person p = (Person)obj;

     return this.age == p.age;

  }

2、String toString():将对象变成字符串,默认返回的格式:类名@哈希值 = getClass().getName() + '@' + Integer.toHexString(hashCode())

  为了对象对应的字符串内容有意义,可以通过复写,建立该类对象自己特有的字符串表现形式。

  public String toString(){

     return "person : "+age;

  }

3、Class getClass():获取任意对象运行时的所属字节码文件对象。

4、int hashCode():返回该对象的哈希码值。支持此方法是为了提高哈希表的性能。将该对象的内部地址转换成一个整数来实现的。

通常equals,toString,hashCode,在应用中都会被覆写,建立具体对象的特有的内容。

------------------------------------------------------------------------------------------------

--< java.lang >-- String字符串

java中用String类进行描述。对字符串进行了对象的封装。这样的好处是可以对字符串这种常见数据进行方便的操作。对象封装后,可以定义N多属性和行为。

如何定义字符串对象呢?String s = "abc";只要是双引号引起的数据都是字符串对象。

特点字符串一旦被初始化,就不可以被改变,存放在方法区中的常量池中。

------------------------------------------------------

String s1 = "abc"; // s1指向的内存中只有一个对象abc。

String s2 = new String("abc"); // s2指向的内容中有两个对象abc、new 。

System.out.println(s1==s2);//false

System.out.println(s1.equals(s2));//true ,字符串中equals比较的是字符串内容是否相同。

-------------------------------------------------------

字符串的方法:

1、构造方法:将字节数组或者字符数组转成字符串。

String s1 = new String();//创建了一个空内容的字符串。

String s2 = null;//s2没有任何对象指向,是一个null常量值。

String s3 = "";//s3指向一个具体的字符串对象,只不过这个字符串中没有内容。

//一般在定义字符串时,不用new。

String s4 = new String("abc");

String s5 = "abc"; 一般用此写法

new String(char[]);//将字符数组转成字符串。

new String(char[],offset,count);//将字符数组中的一部分转成字符串。

2、一般方法:

    2.1 获取:

    ----获取字符串的长度。length();

    ----指定位置的字符。char charAt(int index);

    ----获取指定字符的位置。如果不存在返回-1,所以可以通过返回值-1来判断某一个字符不存在的情况。

           int indexOf(int ch);//返回第一次找到的字符角标

           int indexOf(int ch,int fromIndex); //返回从指定位置开始第一次找到的角标

           int indexOf(String str); //返回第一次找到的字符串角标

           int indexOf(String str,int fromIndex);

           int lastIndexOf(int ch);

           int lastIndexOf(int ch,int fromIndex);

           int lastIndexOf(String str);

           int lastIndexOf(String str,int fromIndex);

     ----获取子串。

           String substring(int start);//从start位开始,到length()-1为止.

String substring(int start,int end);//从start开始到end为止。包

含start位,不包含end位。

           substring(0,str.length());//获取整串

2.2 判断:

    ----字符串中包含指定的字符串吗?

           boolean contains(String substring);

    ----字符串是否以指定字符串开头啊?

           boolean startsWith(string);

    ----字符串是否以指定字符串结尾啊?

           boolean endsWith(string);

    ----判断字符串是否相同

           boolean equals(string);//覆盖了Object中的方法,判断字符串内容是否相同。

    ----判断字符串内容是否相同,忽略大小写。

           boolean equalsIgnoreCase(string) ;

2.3 转换:

    ----通过构造方法可以将字符数组或者字节数组转成字符串。

    ----可以通过字符串中的静态方法,将字符数组转成字符串。

           static String copyValueOf(char[] );

           static String copyValueOf(char[],int offset,int count);

           static String valueOf(char[]);

           static String valueOf(char[],int offset,int count);

    ----将基本数据类型或者对象转成字符串。

           static String valueOf(char);

           static String valueOf(boolean);

           static String valueOf(double);

           static String valueOf(float);

           static String valueOf(int);

           static String valueOf(long);

           static String valueOf(Object);

    ----将字符串转成大小写。

           String toLowerCase();

           String toUpperCase();

    ----将字符串转成数组。

           char[] toCharArray();//转成字符数组。

           byte[] getBytes();//可以加入编码表。转成字节数组。

    ----将字符串转成字符串数组。切割方法。

           String[] split(分割的规则-字符串);

    ----将字符串进行内容替换。注意:修改后变成新字符串,并不是将原字符串直接修改。

           String replace(oldChar,newChar);

           String replace(oldstring,newstring);

    ---- String concat(string); //对字符串进行追加。

           String trim();//去除字符串两端的空格

     int compareTo();//如果参数字符串等于此字符串,则返回值 0;如果此字符串按字典顺序小于字符串参数,则返回一个小于 0 的值;如果此字符串按字典顺序大于字符串参数,则返回一个大于 0 的值。

-------------------------------------------------------------------------------------

--< java.lang >-- StringBuffer字符串缓冲区:

构造一个其中不带字符的字符串缓冲区,初始容量为 16 个字符。

特点:

1、可以对字符串内容进行修改。

2、是一个容器。

3、是可变长度的。

4、缓冲区中可以存储任意类型的数据。

5、最终需要变成字符串。

容器通常具备一些固定的方法:

1、添加。

    StringBuffer append(data):在缓冲区中追加数据。追加到尾部。

    StringBuffer insert(index,data):在指定位置插入数据。

2、删除。

    StringBuffer delete(start,end);删除从start至end-1范围的元素

    StringBuffer deleteCharAt(index);删除指定位置的元素

//sb.delete(0,sb.length());//清空缓冲区。

3、修改。

    StringBuffer replace(start,end,string);将start至end-1替换成string

    void setCharAt(index,char);替换指定位置的字符

    void setLength(len);将原字符串置为指定长度的字符串

4、查找。(查不到返回-1)

    int indexOf(string); 返回指定子字符串在此字符串中第一次出现处的索引。

    int indexOf(string,int fromIndex);从指定位置开始查找字符串

    int lastIndexOf(string); 返回指定子字符串在此字符串中最右边出现处的索引。

    int lastIndexOf(string,int fromIndex); 从指定的索引开始反向搜索

5、获取子串。

    string substring(start); 返回start到结尾的子串

    string substring(start,end); 返回start至end-1的子串

6、反转。

    StringBuffer reverse();字符串反转

---------------------------------------------------------

基本数据类型对象包装类:是按照面向对象思想将基本数据类型封装成了对象。

好处:

1、可以通过对象中的属性和行为操作基本数据。

2、可以实现基本数据类型和字符串之间的转换。

关键字   对应的类名

byte   Byte

short    Short     paserShort(numstring);

int     Integer   静态方法:parseInt(numstring)

long    Long

float   Float

double   Double

char   Character

Boolean  Boolean

基本数据类型对象包装类:都有 XXX parseXXX 方法

只有一个类型没有parse方法:Character

--------------------------------------------------------

Integer对象:★★★☆

数字格式的字符串转成基本数据类型的方法:

1、将该字符串封装成了Integer对象,并调用对象的方法intValue();

2、使用Integer.parseInt(numstring)—>类.方法名:不用建立对象,直接类名调用;

将基本类型转成字符串:Integer中的静态方法 String toString(int);

将一个十进制整数转成其他进制:

    转成二进制:toBinaryString

    转成八进制:toOctalString

    转成十六进制:toHexString

    toString(int num,int radix);

将其他进制转换十进制:

parseInt(string,radix); //将给定的数转成指定的基数进制;

在jdk1.5版本后,对基本数据类型对象包装类进行升级。在升级中,使用基本数据类型对象包装类可以像使用基本数据类型一样,进行运算。

    Integer i = new Integer(4); //1.5版本之前的写法;

    Integer i = 4; //自动装箱,1.5版本后的写法;

    i = i + 5;

    //i对象是不能直接和5相加的,其实底层先将i转成int类型,在和5相加。而转成int类型的操作是隐式的。自动拆箱:拆箱的原理就是i.intValue();i+5运算完是一个int整数。如何赋值给引用类型i呢?其实有对结果进行装箱。

Integer c = 127;

    Integer d = 127;

    System.out.println(c = = d); //true

    //在装箱时,如果数值在byte范围之内,那么数值相同,不会产生新的对象,也就是说多个数值相同的引用指向的是同一个对象。

------------------------------------------------------------------------------------------------

集合:用于存储数据的容器。

特点:

1、对象封装数据,对象多了也需要存储。集合用于存储对象。

2、对象的个数确定可以使用数组,但是不确定怎么办?可以用集合。因为集合是可变长度的。

集合和数组的区别:

1、数组是固定长度的;集合可变长度的。

2、数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。(集合中存放的可都是对象的引用,实际内容都在堆上面或者方法区里面,但是基本数据类型是在栈上分配空间的。随时就被收回的。但是通过自动包装类就可以把基本类型转为对象类型,存放引用就解决了这个问题。)

3、数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。

数据结构:就是容器中存储数据的方式。

对于集合容器,有很多种。因为每一个容器的自身特点不同,其实原理在于每个容器的内部数据结构不同。

集合容器在不断向上抽取过程中。出现了集合体系。

在使用一个体系时,原则:参阅顶层内容。建立底层对象。

java集合类

------------------------------------------------------------

--< java.util >-- Collection接口:

Collection

   |--List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。

   |--Set:无序(存入和取出顺序有可能不一致),不可以存储重复元素。必须保证元素唯一性。

1、添加:

    add(object):添加一个元素

    addAll(Collection) :添加一个集合中的所有元素。

2、删除:

    clear():将集合中的元素全删除,清空集合

    remove(obj) :删除集合中指定的对象。注意:删除成功,集合的长度会改变。

    removeAll(collection) :删除部分元素。部分元素和传入Collection一致。

3、判断:

    boolean contains(obj) :集合中是否包含指定元素 。

    boolean containsAll(Collection) :集合中是否包含指定的多个元素。

    boolean isEmpty():集合中是否有元素。

4、获取:

    int size():集合中有几个元素。

5、取交集:

   boolean  retainAll(Collection) :对当前集合中保留和指定集合中的相同的元素。如果两个集合元素相同,返回flase;如果retainAll修改了当前集合,返回true。

6、获取集合中所有元素:

    Iterator  iterator()迭代器

7、将集合变成数组:

    toArray();

------------------------------------------------------------

--< java.util >-- Iterator接口:

迭代器:是一个接口。作用:用于取集合中的元素。

每一个集合都有自己的数据结构(就是容器中存储数据的方式),都有特定的取出自己内部元素的方式。为了便于操作所有的容器,取出元素。将容器内部的取出方式按照一个统一的规则向外提供,这个规则就是Iterator接口

也就说,只要通过该接口就可以取出Collection集合中的元素,至于每一个具体的容器依据自己的数据结构,如何实现的具体取出细节,这个不用关心,这样就降低了取出元素和具体集合的耦合性。

Iterator it = coll.iterator();//获取容器中的迭代器对象,至于这个对象是是什么不重要。这对象肯定符合一个规则Iterator接口。

-----------------------------------------------------------------------------

例:

public static void main(String[] args) {

       Collection coll = new ArrayList();

       coll.add("abc0");

       coll.add("abc1");

       coll.add("abc2");

       //--------------方式1----------------------

       Iterator it = coll.iterator();

       while(it.hasNext()){

           System.out.println(it.next());

       }

       //---------------方式2----------------------

       for(Iterator it = coll.iterator();it.hasNext(); ){

           System.out.println(it.next());

       }

    }

-----------------------------------------------------------------------------

--< java.util >-- List接口:

List本身是Collection接口的子接口,具备了Collection的所有方法。现在学习List体系特有的共性方法,查阅方法发现List的特有方法都有索引,这是该集合最大的特点。

List有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。

   |--ArrayList底层的数据结构是数组,线程不同步。

   |--LinkedList底层的数据结构是链表,线程不同步,增删元素的速度非常快。

   |--Vector底层的数据结构就是数组,线程同步的,Vector无论查询和增删都巨慢。

1、添加:

    add(index,element) :在指定的索引位插入元素。

    addAll(index,collection) :在指定的索引位插入一堆元素。

2、删除:

    remove(index) :删除指定索引位的元素。 返回被删的元素。

3、获取:

    Object get(index) :通过索引获取指定元素。

    int indexOf(obj) :获取指定元素第一次出现的索引位,如果该元素不存在返回-1;

                       所以,通过-1,可以判断一个元素是否存在。

    int lastIndexOf(Object o) :反向索引指定元素的位置。

    List subList(start,end) :获取子列表。

4、修改:

    Object set(index,element) :对指定索引位进行元素的修改。

5、获取所有元素:

    ListIterator listIterator():list集合特有的迭代器。

List集合支持对元素的增、删、改、查。

List集合因为角标有了自己的获取元素的方式: 遍历。

for(int x=0; x<list.size(); x++){

  sop("get:"+list.get(x));

}

ListIterator是List集合特有的迭代器

ListIterator it = list.listIterator;  //取代Iterator it = list.iterator;

可变长度数组的原理:

当元素超出数组长度,会产生一个新数组,将原数组的数据复制到新数组中,再将新的元素添加到新数组中。

ArrayList:是按照原数组的50%延长。构造一个初始容量为 10 的空列表。

注意:对于list集合,底层判断元素是否相同,其实用的是元素自身的equals方法完成的。所以建议元素都要覆写equals方法,建立元素对象自己的比较相同的条件依据。

--< java.util >-- Set接口

数据结构:数据的存储方式;

Set接口中的方法和Collection中方法一致的。Set接口取出方式只有一种,迭代器

  |--HashSet底层数据结构是哈希表,线程是不同步的无序,高效;

     HashSet集合保证元素唯一性:通过元素的hashCode方法,和equals方法完成的。

     当元素的hashCode值相同时,才继续判断元素的equals是否为true。

     如果为true,那么视为相同元素,不存。如果为false,那么存储。

     如果hashCode值不同,那么不判断equals,从而提高对象比较的速度。

      |--LinkedHashSet有序,hashset的子类。

  |--TreeSet对Set集合中的元素的进行指定顺序的排序。不同步。TreeSet底层的数据结构就是二叉树。

哈希表的原理:(了解一下吧,上课没讲,可以不看)

1、对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值称为哈希值

2、哈希值就是这个元素的位置。

3、如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就存储,在原来对象的哈希值基础 +1顺延。

4、存储哈希值的结构,我们称为哈希表。

5、既然哈希表是根据哈希值存储的,为了提高效率,最好保证对象的关键字是唯一的。

  这样可以尽量少的判断关键字对应的对象是否相同,提高了哈希表的操作效率。

对于ArrayList集合,判断元素是否存在,或者删元素底层依据都是equals方法。

对于HashSet集合,判断元素是否存在,或者删除元素,底层依据的是hashCode方法和equals方法。

Map集合:

|--Hashtable底层是哈希表数据结构,是线程同步的。不可以存储null键,null值。

|--HashMap底层是哈希表数据结构,是线程不同步的。可以存储null键,null值。替代了Hashtable.

|--TreeMap底层是二叉树结构,可以对map集合中的键进行指定顺序的排序。

Map集合存储和Collection有着很大不同:

Collection一次存一个元素;Map一次存一对元素。

Collection是单列集合;Map是双列集合。

Map中的存储的一对元素:一个是键,一个是值,键与值之间有对应(映射)关系。

特点:要保证map集合中键的唯一性。

1、添加。

 put(key,value):当存储的键相同时,新的值会替换老的值,并将老值返回。如果键没有重复,返回null。

  void putAll(Map);

2、删除。

  void clear():清空

  value remove(key) :删除指定键。

3、判断。

  boolean isEmpty():

  boolean containsKey(key):是否包含key

  boolean containsValue(value) :是否包含value

4、取出。

  int size():返回长度

  value get(key) :通过指定键获取对应的值。如果返回null,可以判断该键不存在。当然有特殊情况,就是在hashmap集合中,是可以存储null键null值的。

  Collection values():获取map集合中的所有的值。

5、想要获取map中的所有元素:

  原理:map中是没有迭代器的,collection具备迭代器,只要将map集合转成Set集合,可以使用迭代器了。之所以转成set,是因为map集合具备着键的唯一性,其实set集合就来自于map,set集合底层其实用的就是map的方法。

★  把map集合转成set的方法:

Set keySet();

Set entrySet();//取的是键和值的映射关系。

Entry就是Map接口中的内部接口;

为什么要定义在map内部呢?entry是访问键值关系的入口,是map的入口,访问的是map中的键值对。

---------------------------------------------------------

取出map集合中所有元素的方式一:keySet()方法。

可以将map集合中的键都取出存放到set集合中。对set集合进行迭代。迭代完成,再通过get方法对获取到的键进行值的获取。

     Set keySet = map.keySet();

     Iterator it = keySet.iterator();

    while(it.hasNext()) {

        Object key = it.next();

        Object value = map.get(key);

        System.out.println(key+":"+value);

    }

--------------------------------------------------------

取出map集合中所有元素的方式二:entrySet()方法。

Set entrySet = map.entrySet();

       Iterator it = entrySet.iterator();

       while(it.hasNext()) {

           Map.Entry  me = (Map.Entry)it.next();

           System.out.println(me.getKey()+"::::"+me.getValue());

       }

--------------------------------------------------------

使用集合的技巧:

看到Array就是数组结构,有角标,查询速度很快。

看到link就是链表结构:增删速度快,而且有特有方法。addFirst; addLast; removeFirst(); removeLast(); getFirst();getLast();

看到hash就是哈希表,就要想要哈希值,就要想到唯一性,就要想到存入到该结构的中的元素必须覆盖hashCode,equals方法。

看到tree就是二叉树,就要想到排序,就想要用到比较。

比较的两种方式:

一个是Comparable:覆盖compareTo方法;

一个是Comparator:覆盖compare方法。

LinkedHashSet,LinkedHashMap:这两个集合可以保证哈希表有存入顺序和取出顺序一致,保证哈希表有序。

集合什么时候用?

当存储的是一个元素时,就用Collection。当存储对象之间存在着映射关系时,就使用Map集合。

保证唯一,就用Set。不保证唯一,就用List。

------------------------------------------------------------------------------------------------

Arrays

用于操作数组对象的工具类,里面都是静态方法。

asList方法将数组转换成list集合。

String[] arr = {"abc","kk","qq"};

List<String> list = Arrays.asList(arr);//将arr数组转成list集合。

将数组转换成集合,有什么好处呢?用aslist方法,将数组变成集合;

可以通过list集合中的方法来操作数组中的元素:isEmpty()、contains、indexOf、set;

注意(局限性):数组是固定长度,不可以使用集合对象增加或者删除等,会改变数组长度的功能方法。比如add、remove、clear。(会报不支持操作异常UnsupportedOperationException);

如果数组中存储的引用数据类型,直接作为集合的元素可以直接用集合方法操作。

如果数组中存储的是基本数据类型,asList会将数组实体作为集合元素存在。

集合变数组:用的是Collection接口中的方法:toArray();

------------------------------------------------------------------------------------------------------------------------------------------------

API--- java.lang.System: 属性和行为都是静态的。

long currentTimeMillis(); // 返回当前时间毫秒值 

exit();  // 退出虚拟机

Properties getProperties() ;  // 获取当前系统的属性信息

Properties prop = System.getProperties(); //获取系统的属性信息,并将这些信息存储到Properties集合中。

System.setProperty("myname","老师"); //给系统属性信息集添加具体的属性信息

String name = System.getProperty("os.name");//获取指定属性的信息

//想要知道该系统是否是该软件所支持的系统中的一个。

Set<String> hs = new HashSet<String>();

hs.add("Windows XP");

hs.add("Windows 7");

if(hs.contains(name))

    System.out.println("可以支持");

else

    System.out.println("不支持");

--------------------------------------------------------------------------------------------------------------------

API--- java.lang.Runtime: 类中没有构造方法,不能创建对象。

但是有非静态方法。说明该类中应该定义好了对象,并可以通过一个static方法获取这个对象。用这个对象来调用非静态方法。这个方法就是 static Runtime getRuntime();

--------------------------------------------------------------------------------------------------------------------

API--- java.util.Math: 用于数学运算的工具类,属性和行为都是静态的。该类是final不允许继承。

static double ceil(double a) ; //返回大于指定数值的最小整数

static double floor(double a) ; //返回小于指定数值的最大整数

static long round(double a) ; //四舍五入成整数

static double pow(double a, double b) ; //a的b次幂

static double random(); //返回0~1的伪随机数

例:      public static void main(String[] args) {

              Random r = new Random();

              for(int x=0; x<10; x++) {

                     //double d = Math.floor(Math.random()*10+1);

                     //int d  = (int)(Math.random()*10+1);

                     int d = r.nextInt(10)+1;

                     System.out.println(d);

              }

       }

--------------------------------------------------------------------------------------------------------------------

API--- java.util.Date日期类,月份从0-11;

       例:/*

             1、日期对象和毫秒值之间的转换。

             2、日期对象转成毫秒值。Date类中的getTime方法。

             3、如何将获取到的毫秒值转成具体的日期呢?

              Date类中的setTime方法。也可以通过构造方法。

         */

              //日期对象转成毫秒值

              Date d = new Date();

              long time1 = d.getTime();

              long time2 = System.currentTimeMillis(); / /毫秒值。

             

              //毫秒值转成具体的日期

              long time = 1322709921312l;

              Date d = new Date();

              d.setTime(time);

例:/*

            将日期字符串转换成日期对象:使用的就是DateFormat方法中的  Date parse(String source)

           */

       public static void method() throws Exception {

              String str_time = "2011/10/25";

              DateFormat df = new SimpleDateFormat("yyyy/MM/dd"); //SimpleDateFormat作为可以指定

用户自定义的格式来完成格式化。

              Date d = df.parse(str_time);

       }

       /*

       如果不需要使用特定的格式化风格,完全可以使用DateFormat类中的静态工厂方法获取具体的已经封装好风格的对象。getDateInstance();getDateTimeInstance();

       */

              Date d = new Date();

              DateFormat df = DateFormat.getDateInstance(DateFormat.LONG);

              df = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG);

              String str_time = df.format(d);

       //将日期对象转换成字符串的方式:DateFormat类中的format方法。

           //创建日期格式对象。

              DateFormat df = new SimpleDateFormat(); //该对象的建立内部会封装一个默认的日期格式。

11-12-1下午1:48

              //如果想要自定义日期格式的话。可使用SimpleDateFormat的构造方法。将具体的格式作为参数传入到构造方法中。如何表示日期中年的部分呢?可以必须要参与格式对象文档。

              df = new SimpleDateFormat("yyyyMMdd HH:mm:ss");

              //调用DateFormat中的format方法。对已有的日期对象进行格式化。

              String str_time = df.format(d);

--------------------------------------------------------------------------------------------------------------------

API--- java.util. Calendar日历类

       public static void method(){

              Calendar c = Calendar.getInstance();

              System.out.println(c.get(Calendar.YEAR)+"年"+(c.get(Calendar.MONTH)+1)+"月"

                                          +getNum(c.get(Calendar.DAY_OF_MONTH))+"日"

                                          +"星期"+getWeek(c.get(Calendar.DAY_OF_WEEK)));

       }

       public static String getNum(int num){

              return num>9 ? num+"" : "0"+num;

       }

       public static String getWeek(int index){

       /*

       查表法:建立数据的对应关系.

       最好:数据个数是确定的,而且有对应关系。如果对应关系的一方,是数字,而且可以作为角标,那么可以通过数组来作为表。

       */

              String[] weeks = {"","日","一","二","三","四","五","六"};

              return weeks[index];

       }

------------------------------------------------------------------------------------------------------------------------------------------------

第六章IO

流:可以理解数据的流动,就是一个数据流。

IO用于处理设备上数据。在流中一般以字节的形式存放着数据。

IO流最终要以对象来体现,对象都存在IO包中。IO异常的处理方式:IO一定要写finally

流的分类:

1、输入流(读)和输出流(写)。

2、因为处理的数据不同,分为字节流和字符流。

字节流:处理字节数据的流对象。设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。

为什么要有字符流呢?因为字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时也指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。

注意:流的操作只有两种:读和写。

流的体系因为功能不同,但是有共性内容,不断抽取,形成继承体系。该体系一共有四个基类,而且都是抽象类。即:

字节流:InputStream  OutputStream

字符流:Reader  Writer

在这四个系统中,它们的子类,都有一个共性特点:子类名后缀都是父类名,前缀名都是这个子类的功能名称。

流对象:其实很简单,就是读取和写入。但是因为功能的不同,流的体系中提供N多的对象。那么开始时,到底该用哪个对象更为合适呢?这就需要明确流的操作规律。

流的操作规律:

1,明确源和目的。

       数据读取:InputStream、Reader;

       数据写入:OutputStream、Writer;

2,操作的数据是否是纯文本数据?

       如果是:数据读取:Reader

                  数据写入:Writer

       如果不是:数据读取:InputStream

                    数据写入:OutputStream

3,虽然确定了一个体系,但是该体系中有太多的对象,到底用哪个呢?

       明确操作的数据设备。

       数据输入对应的设备:硬盘(File),内存(数组),键盘(System.in)

       数据输出对应的设备:硬盘(File),内存(数组),控制台(System.out)。

--------------------------------------------------------------------------------------------------------------------

字符流:

Reader用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。

     |---BufferedReader从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。

     |---LineNumberReader跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。

     |---InputStreamReader是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

     |---FileReader用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。

     |---CharArrayReader

     |---StringReader       

-------------------------------------------------

Writer写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。

     |---BufferedWriter将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

     |---OutputStreamWriter是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

     |---FileWriter用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。

     |---PrintWriter

     |---CharArrayWriter

     |---StringWriter

---------------------------------

字节流:

InputStream是表示字节输入流的所有类的超类。

     |--- FileInputStream从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。

     |---FilterInputStream包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。

        |--- BufferedInputStream该类实现缓冲的输入流。

        |--- Stream

 

-----------------------------------------------

OutputStream此抽象类是表示输出字节流的所有类的超类。

     |---FileOutputStream文件输出流是用于将数据写入 FileFileDescriptor 的输出流。

     |---FilterOutputStream此类是过滤输出流的所有类的超类。

        |---BufferedOutputStream该类实现缓冲的输出流。

        |--- PrintStream

        |--- DataOutputStream

 

--------------------------------

缓冲区是提高效率用的,给谁提高呢?

BufferedWriter(将流和缓冲区结合):是给字符输出流提高效率用的,那就意味着,缓冲区对象建立时,必须要先有流对象。明确要提高具体的流对象的效率。

    FileWriter fw = new FileWriter("bufdemo.txt");

    BufferedWriter bufw = new BufferedWriter(fw);//让缓冲区和指定流相关联。

    for(int x=0; x<4; x++){

       bufw.write(x+"abc");

       bufw.newLine(); //写入一个换行符,这个换行符可以依据平台的不同写入不同的换行符。

       bufw.flush();//对缓冲区进行刷新,可以让数据到目的地中。

    }

    bufw.close();//关闭缓冲区,其实就是在关闭具体的流。

-----------------------------

BufferedReader

    FileReader fr = new FileReader("bufdemo.txt");

    BufferedReader bufr  = new BufferedReader(fr);

    String line = null;

    while((line=bufr.readLine())!=null){  //readLine方法返回的时候是不带换行符的。

       System.out.println(line);

    }

    bufr.close();

-----------------------------

//记住,只要一读取键盘录入,就用这句话。

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

    //将读取到的从键盘输入的字节转化成字符存在流中,并将其放入缓冲区

BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));//输出到控制台

String line = null;

while((line=bufr.readLine())!=null){

    if("over".equals(line))

       break;

    bufw.write(line.toUpperCase());//将输入的字符转成大写字符输出

    bufw.newLine();//换行

    bufw.flush();

    }

bufw.close();

bufr.close();

-----------------------------------------------------------------------------------------------

File类:将文件系统中的文件和文件夹封装成了对象。提供了更多的属性和行为可以对这些文件和文件夹进行操作。这些是流对象办不到的,因为流只操作数据。

File类常见方法:

1、创建。

    boolean createNewFile()在指定目录下创建文件,如果该文件已存在,则不创建。而对操作文件的输出流而言,输出流对象已建立,就会创建文件,如果文件已存在,会覆盖。除非续写。

    boolean mkdir()创建此抽象路径名指定的目录。

    boolean mkdirs()创建多级目录。

2、删除。

    boolean delete()删除此抽象路径名表示的文件或目录。

    void deleteOnExit()在虚拟机退出时删除。

注意:在删除文件夹时,必须保证这个文件夹中没有任何内容,才可以将该文件夹用delete删除。

    window的删除动作,是从里往外删。注意:java删除文件不走回收站。要慎用。

3、获取.

    long length()获取文件大小。

    String getName()返回由此抽象路径名表示的文件或目录的名称。

    String getPath()将此抽象路径名转换为一个路径名字符串。

    String getAbsolutePath()返回此抽象路径名的绝对路径名字符串。

    String getParent()返回此抽象路径名父目录的抽象路径名,如果此路径名没有指定父目录,则返回 null

long lastModified()返回此抽象路径名表示的文件最后一次被修改的时间。

File.pathSeparator返回当前系统默认的路径分隔符,windows默认为 “;”。

File.Separator返回当前系统默认的目录分隔符,windows默认为 “\”。

4、判断:

    boolean exists()判断文件或者文件夹是否存在。

    boolean isDirectory()测试此抽象路径名表示的文件是否是一个目录。

    boolean isFile()测试此抽象路径名表示的文件是否是一个标准文件。

    boolean isHidden()测试此抽象路径名指定的文件是否是一个隐藏文件。

    boolean isAbsolute()测试此抽象路径名是否为绝对路径名。

5、重命名。

     boolean renameTo(File dest):可以实现移动的效果。剪切+重命名。

String[] list()列出指定目录下的当前的文件和文件夹的名称。包含隐藏文件。

如果调用list方法的File 对象中封装的是一个文件,那么list方法返回数组为null。如果封装的对象不存在也会返回null。只有封装的对象存在并且是文件夹时,这个list()方法才有效。

------------------------------------------------------------------------------------------------

递归:就是方法自身调用自身。

什么时候用递归呢?

当一个功能被重复使用,而每一次使用该功能时的参数不确定,都由上次的功能元素结果来确定。

简单说:功能内部又用到该功能,但是传递的参数值不确定。(每次功能参与运算的未知内容不确定)。

递归的注意事项:

1、一定要定义递归的条件。

2、递归的次数不要过多。容易出现 StackOverflowError 栈内存溢出错误

其实递归就是在栈内存中不断的加载同一个方法。

FileWriter写入数据的细节:

public static void main(String[] args) {

              FileWriter fw = null;

              try {

                     fw = new FileWriter("demo.txt",true);

                     fw.write("abcde");

              }

              catch (IOException e ){

                     System.out.println(e.toString()+"....");

              }

              finally{

                     if(fw!=null)

                            try{

                                   fw.close();

                            }

                            catch (IOException e){

                                   System.out.println("close:"+e.toString());

                            }

              }

}

--------------------------------------------------------------------------------------------------------------------

FileReader使用Reader体系,读取一个文本文件中的数据。返回 -1 ,标志读到结尾。

例1:

import java.io.*;

class  FileReaderDemo {

       public static void main(String[] args) throws IOException {

              /*

              创建可以读取文本文件的流对象,FileReader让创建好的流对象和指定的文件相关联。

              */

              FileReader fr = new FileReader("demo.txt");

              int ch = 0;

              while((ch = fr.read())!= -1) { //条件是没有读到结尾

                     System.out.println((char)ch); //调用读取流的read方法,读取一个字符。

                     read()方法一次读一个字节的二进制数据int型的!

              }

              fr.close();

       }

}

--------------------------------------------------------------------------------------------------------------------

读取数据的第二种方式:第二种方式较为高效,自定义缓冲区。

例2:

import java.io.*;

class FileReaderDemo2 {

       public static void main(String[] args) throws IOException {

              FileReader fr = new FileReader("demo.txt"); //创建读取流对象和指定文件关联。

              //因为要使用read(char[])方法,将读取到字符存入数组。所以要创建一个字符数组,一般数组的长度都是1024的整数倍。

              char[] buf = new char[1024];//读取的字符数组长度是1024

              int len = 0;

              while(( len=fr.read(buf)) != -1) {

                     System.out.println(new String(buf,0,len));//将char类型的数据从0到len转换成String

              }

              fr.close();

       }

}

close()flush()的区别:

flush():将缓冲区的数据刷到目的地中后,流可以使用。

close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定做。

--------------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------

以下介绍IO包中扩展功能的流对象:

Java.io.outputstream.PrintStream打印流

1、提供了更多的功能,比如打印方法。可以直接打印任意类型的数据。

2、它有一个自动刷新机制,创建该对象,指定参数,对于指定方法可以自动刷新。

3、它使用的本机默认的字符编码.

4、该流的print方法不抛出IOException。

该对象的构造方法。

PrintStream(File file)  :创建具有指定文件且不带自动行刷新的新打印流。

PrintStream(File file, String csn) :创建具有指定文件名称和字符集且不带自动行刷新的新打印流。

PrintStream(OutputStream out) :创建新的打印流。

PrintStream(OutputStream out, boolean autoFlush) :创建新的打印流。

PrintStream(OutputStream out, boolean autoFlush, String encoding) :创建新的打印流。

PrintStream(String fileName) :创建具有指定文件名称且不带自动行刷新的新打印流。

PrintStream(String fileName, String csn)

PrintStream可以操作的目的地:1:File对象。2:字符串路径。3:字节输出流。

前两个都JDK1.5版本才出现。而且在操作文本文件时,可指定字符编码了。

当目的是一个字节输出流时,如果使用的println方法,可以在printStream对象上加入一个true参数。这样对于println方法可以进行自动的刷新,而不是等待缓冲区满了再刷新。最终print方法都将具体的数据转成字符串,而且都对IO异常进行了内部处理。

既然操作的数据都转成了字符串,那么使用PrintWriter更好一些。因为PrintWrite是字符流的子类,可以直接操作字符数据,同时也可以指定具体的编码。

--------------------------------------------------------

PrintWriter具备了PrintStream的特点同时,还有自身特点:

该对象的目的地有四个:1:File对象。2:字符串路径。3:字节输出流。4:字符输出流。

开发时尽量使用PrintWriter。

例;

//读取键盘录入将数据转成大写显示在控制台.

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));//键盘输入

//目的:把数据写到文件中,还想自动刷新。

PrintWriter out = new PrintWriter(new FileWriter("out.txt"),true);//设置true后自动刷新

String line = null;

while((line=bufr.readLine())!=null){

    if("over".equals(line))

       break;

    out.println(line.toUpperCase());//转大写输出

}

    //注意:System.in,System.out这两个标准的输入输出流,在jvm启动时已经存在了。随时可以使用。当jvm结束了,这两个流就结束了。但是,当使用了显示的close方法关闭时,这两个流提前结束了。

out.close();

bufr.close();

-----------------------------------------------------------------------------------------------

对象的序列化目的:将一个具体的对象进行持久化,写入到硬盘上。

注意:静态数据不能被序列化,因为静态数据不在堆内存中,是存储在静态方法区中。

如何将非静态的数据不进行序列化?transient 关键字修饰此变量即可。

Serializable用于启动对象的序列化功能,可以强制让指定类具备序列化功能,该接口中没有成员,这是一个标记接口。这个标记接口用于给序列化类提供UID。这个uid是依据类中的成员的数字签名进行运行获取的。如果不需要自动获取一个uid,可以在类中,手动指定一个名称为serialVersionUID id号。依据编译器的不同,或者对信息的高度敏感性。最好每一个序列化的类都进行手动显示的UID的指定。

例:

import java.io.*;

class ObjectStreamDemo {

    public static void main(String[] args) throws Exception{

       writeObj();

       readObj();

    }

    public static void readObj()throws Exception{

       ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));

       Object obj = ois.readObject();//读取一个对象。

       System.out.println(obj.toString());

    }

    public static void writeObj()throws IOException{

       ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));

       oos.writeObject(new Person("lisi",25)); //写入一个对象。

       oos.close();

    }

}

class Person implements Serializable{

    private static final long serialVersionUID = 42L;

    private transient String name;//用transient修饰后name将不会进行序列化

    public int age;

    Person(String name,int age){

       this.name = name;

       this.age = age;

    }

    public String toString(){

       return name+"::"+age;

    }

}

-----------------------------------------------------------------------------------------------

第七章  多线程

多线程:★★★★

进程:正在进行中的程序。其实进程就是一个应用程序运行时的内存分配空间。

线程:其实就是进程中一个程序执行控制单元,一条执行路径。进程负责的是应用程序的空间的标示。线程负责的是应用程序的执行顺序。

一个进程至少有一个线程在运行,当一个进程中出现多个线程时,就称这个应用程序是多线程应用程序,每个线程在栈区中都有自己的执行空间,自己的方法区、自己的变量。

jvm在启动的时,首先有一个主线程,负责程序的执行,调用的是main方法。主线程执行的代码都在main方法中。

当产生垃圾时,收垃圾的动作,是不需要主线程来完成,因为这样,会出现主线程中的代码执行会停止,会去运行垃圾回收器代码,效率较低,所以由单独一个线程来负责垃圾回收。

随机性的原理:因为cpu的快速切换造成,哪个线程获取到了cpu的执行权,哪个线程就执行。

返回当前线程的名称:Thread.currentThread().getName()

线程的名称是由:Thread-编号定义的。编号从0开始。

线程要运行的代码都统一存放在了run方法中。

线程要运行必须要通过类中指定的方法开启。start方法。(启动后,就多了一条执行路径)

start方法:1)、启动了线程;2)、让jvm调用了run方法。

创建线程的第一种方式:继承Thread ,由子类复写run方法。

步骤:

1,定义类继承Thread类;

2,目的是复写run方法,将要让线程运行的代码都存储到run方法中;

3,通过创建Thread类的子类对象,创建线程对象;

4,调用线程的start方法,开启线程,并执行run方法。

线程状态:

被创建:start()

运行:具备执行资格,同时具备执行权;

冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;

临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;

消亡:stop()

创建线程的第二种方式:实现一个接口Runnable。

步骤:

1,定义类实现Runnable接口。

2,覆盖接口中的run方法(用于封装线程要运行的代码)。

3,通过Thread类创建线程对象;

4,将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构造方法。

为什么要传递呢?因为要让线程对象明确要运行的run方法所属的对象。

5,调用Thread对象的start方法。开启线程,并运行Runnable接口子类中的run方法。

Ticket t = new Ticket();

       /*

       直接创建Ticket对象,并不是创建线程对象。

       因为创建对象只能通过new Thread类,或者new Thread类的子类才可以。

       所以最终想要创建线程。既然没有了Thread类的子类,就只能用Thread类。

       */

       Thread t1 = new Thread(t); //创建线程。

       /*

       只要将t作为Thread类的构造方法的实际参数传入即可完成线程对象和t之间的关联

       为什么要将t传给Thread类的构造方法呢?其实就是为了明确线程要运行的代码run方法。

       */

       t1.start();

      

为什么要有Runnable接口的出现?

1通过继承Thread类的方式,可以完成多线程的建立。但是这种方式有一个局限性,如果一个类已经有了自己的父类,就不可以继承Thread类,因为java单继承的局限性。

可是该类中的还有部分代码需要被多个线程同时执行。这时怎么办呢?

只有对该类进行额外的功能扩展,java就提供了一个接口Runnable。这个接口中定义了run方法,其实run方法的定义就是为了存储多线程要运行的代码。

所以,通常创建线程都用第二种方式。

因为实现Runnable接口可以避免单继承的局限性。

2其实是将不同类中需要被多线程执行的代码进行抽取。将多线程要运行的代码的位置单独定义到接口中。为其他类进行功能扩展提供了前提。

所以Thread类在描述线程时,内部定义的run方法,也来自于Runnable接口。

实现Runnable接口可以避免单继承的局限性。而且,继承Thread,是可以对Thread类中的方法,进行子类覆写的。但是不需要做这个覆写动作的话,只为定义线程代码存放位置,实现Runnable接口更方便一些。所以Runnable接口将线程要执行的任务封装成了对象

-------------------------------------------------------

多线程安全问题的原因

发现一个线程在执行多条语句时,并运算同一个数据时,在执行过程中,其他线程参与进来,并操作了这个数据。导致到了错误数据的产生。

涉及到两个因素:

1,多个线程在操作共享数据。

2,有多条语句对共享数据进行运算。

原因:这多条语句,在某一个时刻被一个线程执行时,还没有执行完,就被其他线程执行了。

解决安全问题的原理

只要将操作共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他线程不能进来执行就可以解决这个问题。

如何进行多句操作共享数据代码的封装呢?

java中提供了一个解决方式:就是同步代码块。

格式:

synchronized(对象) {  // 任意对象都可以。这个对象就是锁。

   需要被同步的代码;

}

---------------------------------------------------------------

同步:★★★★★             //就是在操作共享数据代码时,访问时只能让一个线程进去访问,此线程执行完退出后,别的线程才能再对此共享数据代码进行访问。

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

弊端:相对降低性能,因为判断锁需要消耗资源,产生了死锁。

定义同步是有前提的

1,必须要有两个或者两个以上的线程,才需要同步。

2,多个线程必须保证使用的是同一个锁。

同步的第二种表现形式:      //对共享资源的方法定义同步

同步方法:其实就是将同步关键字定义在方法上,让方法具备了同步性。

同步方法是用的哪个锁呢?     //synchronized(this)用以定义需要进行同步的某一部分代码块

通过验证,方法都有自己所属的对象this,所以同步方法所使用的锁就是this锁。This.方法名

当同步方法被static修饰时,这时的同步用的是哪个锁呢?

静态方法在加载时所属于类,这时有可能还没有该类产生的对象,但是该类的字节码文件加载进内存就已经被封装成了对象,这个对象就是该类的字节码文件对象

所以静态加载时,只有一个对象存在,那么静态同步方法就使用的这个对象。

这个对象就是 类名.class

同步代码块和同步方法的区别?

同步代码块使用的锁可以是任意对象。

同步方法使用的锁是this,静态同步方法的锁是该类的字节码文件对象

在一个类中只有一个同步的话,可以使用同步方法。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。

-------------------------------------------------------

---------------------------------------------------------

同步死锁:通常只要将同步进行嵌套,就可以看到现象。同步方法中有同步代码块,同步代码块中还有同步方法。

线程的停止:通过stop方法就可以停止线程。但是这个方式过时了。

停止线程:原理就是让线程运行的代码结束,也就是结束run方法。

怎么结束run方法?一般run方法里肯定定义循环。所以只要结束循环即可。

第一种方式:定义循环的结束标记。

第二种方式:如果线程处于了冻结状态,是不可能读到标记的,这时就需要通过Thread类中的interrupt方法,将其冻结状态强制清除。让线程恢复具备执行资格的状态,让线程可以读到标记,并结束。

---------< java.lang.Thread >----------

interrupt()中断线程。

setPriority(int newPriority)更改线程的优先级。

getPriority()返回线程的优先级。

toString()返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

Thread.yield()暂停当前正在执行的线程对象,并执行其他线程。

join:临时加入一个线程的时候可以使用join方法。

当A线程执行到了B线程的join方式。A线程处于冻结状态,释放了执行权,B开始执行。A什么时候执行呢?只有当B线程运行结束后,A才从冻结状态恢复运行状态执行。

更多相关推荐:
java各知识点详细总结

Java基础知识总结写代码1明确需求我要做什么2分析思路我要怎么做1233确定步骤每一个思路部分用到哪些语句方法和对象4代码实现用具体的java语言代码把思路体现出来学习新技术的四点1该技术是什么2该技术有什么...

java知识点总结

Java程序设计基础知识点总结1类首字母大写如果一个类名由多个单词构成那么每个单词的首字母都大写中间不使用任何的连接符比如Person类MemberTest类2方法首字母小写如果一个方法由多个单词构成那么第一个...

java知识点总结

目录目录基础篇一JDK常用的包二Get和Post的区别三Java多态的具体体现六九大隐式对象七Forword请求转发与Redirect重定向八JQurey总结九XML和Json的特点十requestgetSes...

Java知识点总结

Java总结1命名规则a必须使用字母数字组成b不能以数字开头c不能使用关键字或保留字11类名所有单词首字母大写其余小写HelloWorld12变量从第二个单词开始首字母大写其余小写使用名词carColor13方...

java总结知识点

Day041switch语句掌握1格式switch表达式case值1语句体1breakcase值2语句体2breakdefault语句体n1break格式解释说明switch说明这是switch语句表达式可以是...

Java 课程知识点复习总结

Java是一种解释性的语言垃圾收集机制自动地不定期检查内存自动删除垃圾JavaApplicationJava应用程序JavaJavaAppletJava小程序Java由类组成类是Java的基本组成单位JavaA...

JAVA SE知识点总结java第一阶段1

知识点总结1Java的三大平台JavaSEJ2SEJavaEEJavaME2Java如何配置环境变量安装gt右键我的电脑gt高级gt环境变量gtpathCProgramFilesJavajdk170bin3Ja...

java面向对象编程知识点总结

java中实现方法重载的条件1在同一类中2方法名称相同参数不相同类型个数顺序3与访问修饰符和返回类型无关4如何区分所要调用方法呢通过参数进行区分方法重载的适用场合多个方法功能相同只是参数不同可以动态根据参数从而...

java基础知识总结

Java基础知识总结写代码1明确需求我要做什么2分析思路我要怎么做1233确定步骤每一个思路部分用到哪些语句方法和对象4代码实现用具体的java语言代码把思路体现出来学习新技术的四点qq群号3288490421...

河北工业大学Java各章知识点总结考试用

网络编程技术课程内容第一章Java语言概述1Java语言特点简单完全面向对象平台无关多线程可靠安全自动垃圾回收机制2Java程序开发流程安装JDK编写源代码javac编译生成字节码文件运行字节码文件3Java两...

java知识点总结2

目录一java简介1二基本语法1三流程控制2四数组3五类和对象4六构造方法5七封装5八继承6九访问修饰符6十多态7十一抽象类7十二接口7一常用类8一java简介定义java是一门面向对象的高级编程语言特点简单性...

《Java知识点总结系列》第四章——函数篇

知识点预览函数的概念函数的语法函数的参数形参实参函数的返回值类型voidreturn函数的嵌套调用函数的递归调用函数的概念1什么是函数方法过程a代码冗余gt程序维护性差b函数方法过程等效函数的语法1形式a返回值...

java各知识点详细总结(16篇)