Java基础知识点

来源:互联网 发布:数据库系统概论第四版 编辑:程序博客网 时间:2024/05/27 00:30

Java基础知识总结

本博文内容参考相关博客以及《Java编程思想》整理而成,如有侵权,请联系博主。

转载请注明出处:http://www.cnblogs.com/BYRans/

PDF版下载链接:《Java基础知识总结》。

如果您想在此基础上做一些修改,请联系dingyu.sdu@gmail.com,我把word原版发给您。

 

一:java概述:

1,JDK:Java Development Kit,java的开发和运行环境,java的开发工具和jre。

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

3,配置环境变量:让java jdk\bin目录下的工具,可以在任意目录下运行,原因是,将该工具所在目录告诉了系统,当使用该工具时,由系统帮我们去找指定的目录。

环境变量的配置:

1):永久配置方式:JAVA_HOME=%安装路径%\Java\jdk

     path=%JAVA_HOME%\bin

    2):临时配置方式:set path=%path%;C:\Program Files\Java\jdk\bin

特点:系统默认先去当前路径下找要执行的程序,如果没有,再去path中设置的路径下找。

classpath的配置:

1):永久配置方式:classpath=.;c:\;e:\

    2):临时配置方式:set classpath=.;c:\;e:\

 

注意:在定义classpath环境变量时,需要注意的情况

如果没有定义环境变量classpath,java启动jvm后,会在当前目录下查找要运行的类文件;

如果指定了classpath,那么会在指定的目录下查找要运行的类文件。

还会在当前目录找吗?两种情况:

    

CLASSPATH是什么?它的作用是什么?

它是javac编译器的一个环境变量。它的作用与import、package关键字有关。当你写下improt java.util.*时,编译器面对import关键字时,就知道你要引入java.util这个package中的类;但是编译器如何知道你把这个package放在哪里了呢?所以你首先得告诉编译器这个package的所在位置;如何告诉它呢?就是设置CLASSPATH啦 :) 如果java.util这个package在c:/jdk/ 目录下,你得把c:/jdk/这个路径设置到CLASSPATH中去!当编译器面对import java.util.*这个语句时,它先会查找CLASSPATH所指定的目录,并检视子目录java/util是否存在,然后找出名称吻合的已编译文件(.class文件)。如果没有找到就会报错!CLASSPATH有点像c/c++编译器中的INCLUDE路径的设置哦,是不是?当c/c++编译器遇到include 这样的语句,它是如何运作的?哦,其实道理都差不多!搜索INCLUDE路径,检视文件!当你自己开发一个package时,然后想要用这个package中的类;自然,你也得把这个package所在的目录设置到CLASSPATH中去!CLASSPATH的设定,对JAVA的初学者而言是一件棘手的事。所以Sun让JAVA2的JDK更聪明一些。你会发现,在你安装之后,即使完全没有设定CLASSPATH,你仍然能够编译基本的JAVA程序,并且加以执行。

 

PATH环境变量

PATH环境变量。作用是指定命令搜索路径,在命令行下面执行命令如javac编译java程序时,它会到PATH变量所指定的路径中查找看是否能找到相应的命令程序。我们需要把jdk安装目录下的bin目录增加到现有的PATH变量中,bin目录中包含经常要用到的可执行文件如javac/java/javadoc等待,设置好PATH变量后,就可以在任何目录下执行javac/java等工具了。

 

4,javac命令和java命令做什么事情呢?

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

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

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

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

 

二:java语法基础:

 

  1. 标示符:

    1),数字不可以开头。

    2),不可以使用关键字。

 

  1. 变量的作用域和生存期:

    变量的作用域:

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

    生命周期:

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

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

     

  2. 数据类型:

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

简单类型

boolean 

byte 

char 

short 

int

long

float 

double 

void 

二进制位数

1 

8 

16 

16 

32 

64 

32 

64 

-- 

封装器类

Boolean 

Byte 

Character 

Short 

Integer 

Long 

Float 

Double 

Void 

 

  1. 运算符号:

    4)、逻辑运算符。

        & | ^ ! && ||

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

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

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

        ^:异或:和或有点不一样。

             两边结果一样,就为false。

             两边结果不一样,就为true.

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

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

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

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

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

        & | ^

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

    练习:对两个变量的数据进行互换。不需要第三方变量。

            int a = 3,b = 5;-->b = 3,a = 5;

        方法一:

            a = a + b; a = 8;

            b = a - b; b = 3;

            a = a - b; a = 5;

        方法二:

            a = a ^ b;//

            b = a ^ b;//b = a ^ b ^ b = a

            a = a ^ b;//a = a ^ b ^ a = b;

        练习:高效的算出 2*8 = 2<<3;

 

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

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

重写:父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。

 

  1. Java内存管理

    Java内存管理:深入Java内存区域

  Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来。

  1. 概述:

  对于从事C和C++程序开发的开发人员来说,在内存管理领域,他们既是拥有最高权力的皇帝,又是从事最基础工作的劳动人民—既拥有每一个对象的"所有权",又担负着每一个对象生命开始到终结的维护责任。

对于Java程序员来说,在虚拟机的自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,而且不容易出现内存泄漏和内存溢出问题,看起来由虚拟机管理内存一切都很美好。不过,也正是因为Java程序员把内存控制的权力交给了Java虚拟机,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那排查错误将会成为一项异常艰难的工作。

  1. 运行时数据区域

  Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。根据《Java虚拟机规范(第2版)》的规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域,如下图所示:

          

  1. 程序计数器     

  程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。 由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为"线程私有"的内存。 如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Natvie方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

  1. Java虚拟机栈

  与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

经常有人把Java内存区分为堆内存(Heap)和栈内存(Stack),这种分法比较粗糙,Java内存区域的划分实际上远比这复杂。这种划分方式的流行只能说明大多数程序员最关注的、与对象内存分配关系最密切的内存区域是这两块。其中所指的""在后面会专门讲述,而所指的""就是现在讲的虚拟机栈,或者说是虚拟机栈中的局部变量表部分。

局部变量表存放了编译期可知的各种基本数据类型(booleanbytecharshortintfloatlongdouble)、对象引用(reference类型),它不等同于对象本身,根据不同的虚拟机实现,它可能是一个指向对象起始地址的引用指针,也可能指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。

其中64位长度的longdouble类型的数据会占用2个局部变量空间Slot),其余的数据类型只占用1个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。 Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。

  1. 本地方法栈

  本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowErrorOutOfMemoryError异常。

  1. Java堆

  对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配,但是随着JIT编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也渐渐变得不是那么"绝对"了。

  Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做"GC"Garbage Collected Heap,幸好国内没翻译成"垃圾堆")。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。如果从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区Thread Local Allocation BufferTLAB)。不过,无论如何划分,都与存放内容无关,无论哪个区域,存储的都仍然是对象实例,进一步划分的目的是为了更好地回收内存,或者更快地分配内存。在本章中,我们仅仅针对内存区域的作用进行讨论,Java堆中的上述各个区域的分配和回收等细节将会是下一章的主题。

  根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx-Xms控制)。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

  1. 方法区

  方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。

  对于习惯在HotSpot虚拟机上开发和部署程序的开发者来说,很多人愿意把方法区称为"永久代"Permanent Generation),本质上两者并不等价,仅仅是因为HotSpot虚拟机的设计团队选择把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已。对于其他虚拟机(如BEA JRockitIBM J9等)来说是不存在永久代的概念的。即使是HotSpot虚拟机本身,根据官方发布的路线图信息,现在也有放弃永久代并"搬家"Native Memory来实现方法区的规划了。

  Java虚拟机规范对这个区域的限制非常宽松,除了和Java堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样"永久"存在了。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,一般来说这个区域的回收"成绩"比较难以令人满意,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收确实是有必要的。在Sun公司的BUG列表中,  曾出现过的若干个严重的BUG就是由于低版本的HotSpot虚拟机对此区域未完全回收而导致内存泄漏。根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

  1. 运行时常量池

  运行时常量池(Runtime Constant Pool)是方法区的一部分Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。 Java虚拟机对Class文件的每一部分(自然也包括常量池)的格式都有严格的规定,每一个字节用于存储哪种数据都必须符合规范上的要求,这样才会被虚拟机认可、装载和执行。但对于运行时常量池,Java虚拟机规范没有做任何细节的要求,不同的提供商实现的虚拟机可以按照自己的需要来实现这个内存区域。不过,一般来说,除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只能在编译期产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。既然运行时常量池是方法区的一部分,自然会受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。

  1. 对象访问

  介绍完Java虚拟机的运行时数据区之后,我们就可以来探讨一个问题:在Java语言中,对象访问是如何进行的?对象访问在Java语言中无处不在,是最普通的程序行为,但即使是最简单的访问,也会却涉及Java栈、Java堆、方法区这三个最重要内存区域之间的关联关系,如下面的这句代码:

          Object obj = new Object();

假设这句代码出现在方法体中,那"Object obj"这部分的语义将会反映到Java栈的本地变量表中,作为一个reference类型数据出现。而"new Object()"这部分的语义将会反映到Java堆中,形成一块存储了Object类型所有实例数据值(Instance Data,对象中各个实例字段的数据)的结构化内存,根据具体类型以及虚拟机实现的对象内存布局(Object Memory Layout)的不同,这块内存的长度是不固定的。另外,在Java堆中还必须包含能查找到此对象类型数据(如对象类型、父类、实现的接口、方法等)的地址信息,这些类型数据则存储在方法区中。

  由于reference类型在Java虚拟机规范里面只规定了一个指向对象的引用,并没有定义这个引用应该通过哪种方式去定位,以及访问到Java堆中的对象的具体位置,因此不同虚拟机实现的对象访问方式会有所不同,主流的访问方式有两种:使用句柄和直接指针 如果使用句柄访问方式Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息,如下图所示:

      

  如果使用的是直接指针访问方式Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,reference中直接存储的就是对象地址,如下图所示:

      

  这两种对象的访问方式各有优势,使用句柄访问方式的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要被修改。使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销,由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。就本书讨论的主要虚拟机Sun HotSpot而言,它是使用第二种方式进行对象访问的,但从整个软件开发的范围来看,各种语言和框架使用句柄来访问的情况也十分常见。

 

三:面向对象:★★★★★

匿名对象使用场景

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

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

 

类中怎么没有定义主函数呢?

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

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

 

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

1:成员变量直接定义在类中。

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

2:成员变量在这个类中有效。

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

3:成员变量存在于堆内存中,随着对象的产生而存在,消失而消失。

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

 

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

特点

1该函数的名称和所在类的名称相同。

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

3该函数没有具体的返回值。

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

 

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

 

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

 

构造代码块和构造函数有什么区别?

构造代码块:是给所有的对象进行初始化,也就是说,所有的对象都会调用一个代码块。只要对象一建立。就会调用这个代码块。

构造函数:是给与之对应的对象进行初始化。它具有针对性。

  1. 执行顺序:(优先级从高到低。)静态代码块>mian方法>构造代码块>构造方法。其中静态代码块只执行一次。构造代码块在每次创建对象是都会执行。
  2. 静态代码块的作用:比如我们在调用C语言的动态库时会可把.so文件放在此处。 
  3. 构造代码块的功能:(可以把不同构造方法中相同的共性的东西写在它里面)。例如:比如不论任何机型的电脑都有开机这个功能,此时我们就可以把这个功能定义在构造代码块内。

 

Person p = new Person();

创建一个对象都在内存中做了什么事情?

1先将硬盘上指定位置的Person.class文件加载进内存。

2执行main方法时,在栈内存中开辟了main方法的空间(压栈-进栈),然后在main方法的栈区分配了一个变量p。

3在堆内存中开辟一个实体空间,分配了一个内存首地址值。new

4在该实体空间中进行属性的空间分配,并进行了默认初始化。

5对空间中的属性进行显示初始化。

6进行实体的构造代码块初始化。

7调用该实体对应的构造函数,进行构造函数初始化。()

8将首地址赋值给p ,p变量就引用了该实体。(指向了该对象)

 

 

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

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

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

 

this:代表对象。就是所在函数所属对象的引用。

this到底代表什么呢?哪个对象调用了this所在的函数,this就代表哪个对象,就是哪个对象的引用。

开发时,什么时候使用this呢?

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

 

this 还可以用于构造函数间的调用。

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

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

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

 

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

 

static:★★★ 关键字,是一个修饰符,用于修饰成员(成员变量和成员函数)。

特点:

1、static变量

 按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。两者的区别是:

 对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。

 对于实例变量,没创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。

2、静态方法

 静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。因为实例成员与特定的对象关联!这个需要去理解,想明白其中的道理,不是记忆!!!

 因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。

3、static代码块

 static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。

4、static和final一块用表示什么

static final用来修饰成员变量和成员方法,可简单理解为"全局常量"!

对于变量,表示一旦给值就不可修改,并且通过类名可以访问。

对于方法,表示不可覆盖,并且可以通过类名直接访问。

 

备注:

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

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

(这句话是针对同一个类环境下的,比如说,一个类有多个成员(属性,方法,字段),静态方法A,那么可以访问同类名下其他静态成员,你如果访问非静态成员就不行)

因为静态方法加载时,优先于对象存在,所以没有办法访问对象中的成员。

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

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

4,主函数是静态的。

 

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

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

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

2,成员变量存在于堆内存中。

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

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

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

4,成员变量只能被对象所调用 。

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

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

 

静态代码块:就是一个有静态关键字标示的一个代码块区域。定义在类中。

作用:可以完成类的初始化。静态代码块随着类的加载而执行,而且只执行一次(new 多个对象就只执行一次)。如果和主函数在同一类中,优先于主函数执行。

 

final

 根据程序上下文环境,Java关键字final有"这是无法改变的"或者"终态的"含义,它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变、设计或效率。

final类不能被继承,没有子类,final类中的方法默认是final的。

final方法不能被子类的方法覆盖,但可以被继承。

final成员变量表示常量,只能被赋值一次,赋值后值不再改变。

final不能用于修饰构造方法。

注意:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。

1、final类

final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会载被扩展,那么就设计为final类。

2、final方法

如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final方法。

使用final方法的原因有二:

第一、把方法锁定,防止任何继承类修改它的意义和实现。

第二、高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。

3、final变量(常量)

 用final修饰的成员变量表示常量,值一旦给定就无法改变!

 final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。

 从下面的例子中可以看出,一旦给final变量初值后,值就不能再改变了。

 另外,final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初始化。但是,final空白在final关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。

4、final参数

当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。

 

 

生成Java帮助文档:命令格式:javadoc –d 文件夹名 –auther –version *.java

/** //格式

*类描述

*@author 作者名

*@version 版本号

*/

/**

*方法描述

*@param 参数描述

*@return 返回值描述

*/

 

 

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

java中对于继承,java只支持单继承。java虽然不直接支持多继承,但是可实现多接口。

 

1:成员变量。

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

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

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

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

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

2:成员函数。

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

3:构造函数。

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

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

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

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

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

 

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

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

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

 

问题:

super()和this()是否可以同时出现的构造函数中?

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

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

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

 

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

1:子类覆盖父类时,必须要保证,子类方法的权限必须大于等于父类方法权限可以实现继承。否则,编译失败。(举个例子,在父类中是public的方法,如果子类中将其降低访问权限为private,那么子类中重写以后的方法对于外部对象就不可访问了,这个就破坏了继承的含义)

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

 

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

这时如何解决问题呢?介绍一个关键字,final

 

final特点:(详细解释见前面)

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

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

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

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

 

抽象类: 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中是有多继承的。接口与接口之间存在着继承关系,接口可以多继承接口

java类是单继承的。classB Extends classA

java接口可以多继承。Interface3 Extends Interface0, Interface1, interface……

不允许类多重继承的主要原因是,如果A同时继承B和C,而b和c同时有一个D方法,A如何决定该继承那一个呢?

但接口不存在这样的问题,接口全都是抽象方法继承谁都无所谓,所以接口可以继承多个接口。

 

抽象类与接口:

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

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

 

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

 

抽象类和接口的区别:

1:抽象类只能被继承,而且只能单继承。

接口需要被实现,而且可以多实现。

2:抽象类中可以定义非抽象方法,子类可以直接继承使用。

接口中都是抽象方法,需要子类去实现。

3:抽象类使用的是 is a 关系。

接口使用的 like a 关系。

4:抽象类的成员修饰符可以自定义。

接口中的成员修饰符是固定的。全都是public的。

 

多 态★★★★★

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

 

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

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

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

多态的前提:

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

    2:通常会有覆盖操作。

 

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

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

 

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

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

 

-------------------------------------------------------------------------------------java.lang.Object

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

 

具体方法:

  1. boolean equals(Object obj):用于比较两个对象是否相等,其实内部比较的就是两个对象地址。

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

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

    public String toString(){

        return "person : "+age;

    }

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

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

 

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

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

 

内部类:如果A类需要直接访问B类中的成员,而B类又需要建立A类的对象。这时,为了方便设计和访问,直接将A类定义在B类中。就可以了。A类就称为内部类。内部类可以直接访问外部类中的成员。而外部类想要访问内部类,必须要建立内部类的对象。

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

class Outer{

    int num = 4;    

    class Inner {

        void show(){

            System.out.println("inner show run "+num);

        }

    }

    public void method(){

        Inner in = new Inner();//创建内部类的对象。

        in.show();//调用内部类的方法。 //内部类直接访问外部类成员,用自己的实例对象;

    }                                        //外部类访问内部类要定义内部类的对象;

}

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

当内部类定义在外部类中的成员位置上,可以使用一些成员修饰符修饰 private、static。

1:默认修饰符。

直接访问内部类格式:外部类名.内部类名 变量名 = 外部类对象.内部类对象;

Outer.Inner in = new Outer.new Inner();//这种形式很少用。

    但是这种应用不多见,因为内部类之所以定义在内部就是为了封装。想要获取内部类对象通常都通过外部类的方法来获取。这样可以对内部类对象进行控制。

2:私有修饰符。

    通常内部类被封装,都会被私有化,因为封装性不让其他程序直接访问。

3:静态修饰符。

    如果内部类被静态修饰,相当于外部类,会出现访问局限性,只能访问外部类中的静态成员。

    注意;如果内部类中定义了静态成员,那么该内部类必须是静态的。

 

内部类编译后的文件名为:"外部类名$内部类名.java";

 

为什么内部类可以直接访问外部类中的成员呢?

那是因为内部中都持有一个外部类的引用。这个是引用是 外部类名.this

内部类可以定义在外部类中的成员位置上,也可以定义在外部类中的局部位置上。

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

 

匿名内部类(对象):没有名字的内部类。就是内部类的简化形式。一般只用一次就可以用这种形式。匿名内部类其实就是一个匿名子类对象想要定义匿名内部类:需要前提,内部类必须继承一个类或者实现接口。

 

匿名内部类的格式:new 父类名&接口名(){ 定义子类成员或者覆盖父类方法 }.方法。

 

匿名内部类的使用场景:

当函数的参数是接口类型引用时,如果接口中的方法不超过3个。可以通过匿名内部类来完成参数的传递。

其实就是在创建匿名内部类时,该类中的封装的方法不要过多,最好两个或者两个以内。

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

//面试

        //1

        new Object(){

            void show(){

                System.out.println("show run");                

            }

        }.show();                                    //写法和编译都没问题

        //2

        Object obj = new Object(){

            void show(){

                System.out.println("show run");

            }

        };

        obj.show();                                //写法正确,编译会报错

        

        1和2的写法正确吗?有区别吗?说出原因。

        写法是正确,1和2都是在通过匿名内部类建立一个Object类的子类对象。

        区别:

        第一个可是编译通过,并运行。

        第二个编译失败,因为匿名内部类是一个子类对象,当用Object的obj引用指向时,就被提升为了Object类型,而编译时会检查Object类中是否有show方法,此时编译失败。

 

 

异 常:★★★★

--java.lang.Throwable:

Throwable:可抛出的。

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

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

 

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

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

 

throw与throws区别:

throws是用来声明一个方法可能抛出的所有异常信息,而throw则是指抛出的一个具体的异常类型。此外throws是将异常声明但是不处理,而是将异常往上传,谁调用我就交给谁处理。

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

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

 

throws格式:方法名(参数)throws 异常类1,异常类2,.....

throw:就是自己进行异常处理,处理的时候有两种方式,要么自己捕获异常(也就是try catch进行捕捉),要么声明抛出一个异常(就是throws 异常~~)。

 

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

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

try {

    需要被检测的代码;

}

catch(异常类 变量名){

    异常处理代码;

}

fianlly{

    一定会执行的代码;

}

 

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

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

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

 

自定义异常的步骤:

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

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

 

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

 

try catch finally的几种结合方式:

1,

try

catch

finally

 

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

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

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

 

注意:

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

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

 

 

多线程:★★★★

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

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

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

 

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

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

 

Thread类中run()和start()方法的区别:

start():用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。

run():run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。

总结:start()方法最本质的功能是从CPU中申请另一个线程空间来执行 run()方法中的代码,它和当前的线程是两条线,在相对独立的线程空间运行,也就是说,如果你直接调用线程对象的run()方法,当然也会执行,但那是 在当前线程中执行,run()方法执行完成后继续执行下面的代码.而调用start()方法后,run()方法的代码会和当前线程并发(单CPU)或并行 (多CPU)执行。所以请记住一句话:调用线程对象的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接口将线程要执行的任务封装成了对象

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

//面试

        new Thread(new Runnable(){ //匿名

            public void run(){

                System.out.println("runnable run");    

            }

        })

 

        {

            public void run(){

                System.out.println("subthread run");

            }

        }.start(); //结果:subthread run

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

synchronized关键字(一)

一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

五、以上规则对其它对象锁同样适用.

 

package ths;

public class Thread1 implements Runnable {

public void run() {

synchronized(this) {

for (int i = 0; i < 5; i++) {

System.out.println(Thread.currentThread().getName()+"synchronized loop " + i);

}

}

}

}

 

synchronized关键字(二)

synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。

1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:

public synchronized void accessVal(int newVal);

synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。

在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。

synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。

2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:

synchronized(syncObject) {

//允许访问控制的代码

}

synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

对synchronized(this)的一些理解

一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

五、以上规则对其它对象锁同样适用。

 

解决安全问题的原理

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

如何保障共享数据的线程安全呢?

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

格式:

synchronized(对象) { //任意对象都可以。这个对象就是共享数据。

    需要被同步的代码;

}

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

同步:★★★★★

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

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

 

 

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

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

 

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

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

 

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

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

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

这个对象就是 类名.class

 

同步代码块和同步函数的区别?

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

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

 

在一个类中只有一个同步的话,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。

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

★考点问题:请写一个延迟加载的单例模式?写懒汉式;当出现多线程访问时怎么解决?加同步,解决安全问题;效率高吗?不高;怎样解决?通过双重判断的形式解决。

//懒汉式:延迟加载方式。

当多线程访问懒汉式时,因为懒汉式的方法内对共性数据进行多条语句的操作。所以容易出现线程安全问题。为了解决,加入同步机制,解决安全问题。但是却带来了效率降低。

为了效率问题,通过双重判断的形式解决。

class Single{

    private static Single s = null;

    private Single(){}

    public static Single getInstance(){ //锁是谁?字节码文件对象;

        if(s == null){

            synchronized(Single.class){

                if(s == null)

                    s = new Single();

            }

        }

        return s;

    }

}

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

等待唤醒机制:涉及的方法:

wait:将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。

notify:唤醒线程池中某一个等待线程。

notifyAll:唤醒的是线程池中的所有线程。

 

注意:

1:这些方法都需要定义在同步中

2:因为这些方法必须要标示所属的锁。

    你要知道 A锁上的线程被wait了,那这个线程就相当于处于A锁的线程池中,只能A锁的notify唤醒。

3:这三个方法都定义在Object类中。为什么操作线程的方法定义在Object类中?

    因为这三个方法都需要定义同步内,并标示所属的同步锁,既然被锁调用,而锁又可以是任意对象,那么能被任意对象调用的方法一定定义在Object类中。

 

wait和sleep区别: 分析这两个方法:从执行权和锁上来分析:

wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。

sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。

wait:线程会释放执行权,而且线程会释放锁。

sleep:线程会释放执行权,但不是不释放锁。

 

线程的停止:通过stop方法就可以停止线程。但是这个方式过时了。

停止线程:原理就是:让线程运行的代码结束,也就是结束run方法。

怎么结束run方法?一般run方法里肯定定义循环。所以只要结束循环即可。

第一种方式:定义循环的结束标记。

第二种方式:如果线程处于了冻结状态,是不可能读到标记的,这时就需要通过Thread类中的interrupt方法,将其冻结状态强制清除。让线程恢复具备执行资格的状态,让线程可以读到标记,并结束。

 

---------< java.lang.Thread >----------

interrupt():中断线程。

setPriority(int newPriority):更改线程的优先级。

getPriority():返回线程的优先级。

toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

Thread.yield():暂停当前正在执行的线程对象,并执行其他线程。

setDaemon(true):将该线程标记为守护线程或用户线程。将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。

join临时加入一个线程的时候可以使用join方法。

当A线程执行到了B线程的join方式。A线程处于冻结状态,释放了执行权,B开始执行。A什么时候执行呢?只有当B线程运行结束后,A才从冻结状态恢复运行状态执行。

 

 

LOCK的出现替代了同步:lock.lock();………lock.unlock();

Lock接口:多线程在JDK1.5版本升级时,推出一个接口Lock接口

解决线程安全问题使用同步的形式,(同步代码块,要么同步函数)其实最终使用的都是锁机制。

 

到了后期版本,直接将锁封装成了对象。线程进入同步就是具备了锁,执行完,离开同步,就是释放了锁。

在后期对锁的分析过程中,发现,获取锁,或者释放锁的动作应该是锁这个事物更清楚。所以将这些动作定义在了锁当中,并把锁定义成对象。

 

所以同步是隐示的锁操作,而Lock对象是显示的锁操作,它的出现就替代了同步。

 

在之前的版本中使用Object类中wait、notify、notifyAll的方式来完成的。那是因为同步中的锁是任意对象,所以操作锁的等待唤醒的方法都定义在Object类中。

 

而现在锁是指定对象Lock。所以查找等待唤醒机制方式需要通过Lock接口来完成。而Lock接口中并没有直接操作等待唤醒的方法,而是将这些方式又单独封装到了一个对象中。这个对象就是Condition,将Object中的三个方法进行单独的封装。并提供了功能一致的方法await()、signal()、signalAll()体现新版本对象的好处。

< java.util.concurrent.locks > Condition接口:await()、signal()、signalAll();

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

class BoundedBuffer {

final Lock lock = new ReentrantLock();

final Condition notFull = lock.newCondition();

final Condition notEmpty = lock.newCondition();

final Object[] items = new Object[100];

int putptr, takeptr, count;

public void put(Object x) throws InterruptedException {

lock.lock();

try {

while (count == items.length)

notFull.await();

items[putptr] = x;

if (++putptr == items.length) putptr = 0;

++count;

notEmpty.signal();

}

    finally {

lock.unlock();

}

}

public Object take() throws InterruptedException {

lock.lock();

try {

while (count == 0)

notEmpty.await();

Object x = items[takeptr];

if (++takeptr == items.length) takeptr = 0;

--count;

notFull.signal();

return x;

}

finally {

lock.unlock();

}

}

}

 

集合框架

集合框架:★★★★★,用于存储数据的容器。

 

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

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

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

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

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

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

 

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

    |--ArrayList:底层的数据结构是数组,线程不同步,ArrayList替代了Vector,查询元素的速度非常快。

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

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

 

 

可变长度数组的原理:

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

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

Vector:是按照原数组的100%延长。

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

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

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

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

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

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

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

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

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

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

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

 

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

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

 

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

Map集合:

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

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

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

 

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

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

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

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

特点:要保证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());

        }

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

 

将非同步集合转成同步集合的方法:Collections中的 XXX synchronizedXXX(XXX);

List synchronizedList(list);

Map synchronizedMap(map);

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {

return new SynchronizedMap<K,V>(m);

}

原理:定义一个类,将集合所有的方法加同一把锁后返回。

List list = Collections.synchronizedList(new ArrayList());

Map<String,String> synmap = Collections.synchronizedMap(map);

 

Collection 和 Collections的区别:

Collections是个java.util下的类,是针对集合类的一个工具类,提供一系列静态方法,实现对集合的查找、排序、替换、线程安全化(将非同步的集合转换成同步的)等操作。

Collection是个java.util下的接口,它是各种集合结构的父接口,继承于它的接口主要有Set和List,提供了关于集合的一些操作,如插入、删除、判断一个元素是否其成员、遍历等。

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

自动拆装箱:java中数据类型分为两种 : 基本数据类型 引用数据类型(对象)

在 java程序中所有的数据都需要当做对象来处理,针对8种基本数据类型提供了包装类,如下:

int --> Integer

byte --> Byte

short --> Short

long --> Long

char --> Character

double --> Double

float --> Float

boolean --> Boolean

 

jdk5以前基本数据类型和包装类之间需要互转:

基本---引用 Integer x = new Integer(x);

引用---基本 int num = x.intValue();

1)、Integer x = 1; x = x + 1; 经历了什么过程?装箱 à 拆箱 à 装箱

2)、为了优化,虚拟机为包装类提供了缓冲池,Integer池的大小 -128~127 一个字节的大小

3)、String池:Java为了优化字符串操作 提供了一个缓冲池;

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

泛型:jdk1.5版本以后出现的一个安全机制。表现格式:< >

好处:

1:将运行时期的问题ClassCastException问题转换成了编译失败,体现在编译时期,程序员就可以解决问题。

2:避免了强制转换的麻烦。

 

泛型中的通配符:可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。

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

 

反射技术

反射技术:其实就是动态加载一个指定的类,并获取该类中的所有的内容。并将字节码文件中的内容都封装成对象,这样便于操作这些成员。简单说:反射技术可以对一个类进行解剖。

 

反射的好处:大大的增强了程序的扩展性。

 

反射的基本步骤:

1、获得Class对象,就是获取到指定的名称的字节码文件对象。

2、实例化对象,获得类的属性、方法或构造函数。

3、访问属性、调用方法、调用构造函数创建对象。

 

获取这个Class对象,有三种方式:

1:通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。

2:每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。

     前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。

3:使用的Class类中的方法,静态的forName方法

     指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可。

// 1. 根据给定的类名来获得 用于类加载

String classname = "cn.itcast.reflect.Person";// 来自配置文件

Class clazz = Class.forName(classname);// 此对象代表Person.class

// 2. 如果拿到了对象,不知道是什么类型 用于获得对象的类型

Object obj = new Person();

Class clazz1 = obj.getClass();// 获得对象具体的类型

// 3. 如果是明确地获得某个类的Class对象 主要用于传参

Class clazz2 = Person.class;    

 

反射的用法

1)、需要获得java类的各个组成部分,首先需要获得类的Class对象,获得Class对象的三种方式:

    Class.forName(classname)    用于做类加载

    obj.getClass()                用于获得对象的类型

    类名.class             用于获得指定的类型,传参用

 

2)、反射类的成员方法:

    Class clazz = Person.class;

    Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});

    method.invoke();

    

3)、反射类的构造函数:

    Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})

    con.newInstance(params...)

 

4)、反射类的属性:

    Field field = clazz.getField(fieldName);

    field.setAccessible(true);

    field.setObject(value);

 

获取了字节码文件对象后,最终都需要创建指定类的对象:

创建对象的两种方式(其实就是对象在进行实例化时的初始化方式):

1,调用空参数的构造函数:使用了Class类中的newInstance()方法。

2,调用带参数的构造函数:先要获取指定参数列表的构造函数对象,然后通过该构造函数的对象的newInstance(实际参数) 进行对象的初始化。

 

综上所述,第二种方式,必须要先明确具体的构造函数的参数类型,不便于扩展。所以一般情况下,被反射的类,内部通常都会提供一个公有的空参数的构造函数。

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

    // 如何生成获取到字节码文件对象的实例对象。

        Class clazz = Class.forName("cn.itcast.bean.Person");//类加载

// 直接获得指定的类型

        clazz = Person.class;

        // 根据对象获得类型

        Object obj = new Person("zhangsan", 19);

        clazz = obj.getClass();

 

        Object obj = clazz.newInstance();//该实例化对象的方法调用就是指定类中的空参数构造函数,给创建对象进行初始化。当指定类中没有空参数构造函数时,该如何创建该类对象呢?请看method_2();

    public static void method_2() throws Exception {

        Class clazz = Class.forName("cn.itcast.bean.Person");

        //既然类中没有空参数的构造函数,那么只有获取指定参数的构造函数,用该函数来进行实例化。

        //获取一个带参数的构造器。

        Constructor constructor = clazz.getConstructor(String.class,int.class);

        //想要对对象进行初始化,使用构造器的方法newInstance();

        Object obj = constructor.newInstance("zhagnsan",30);

        //获取所有构造器。

        Constructor[] constructors = clazz.getConstructors();//只包含公共的

        constructors = clazz.getDeclaredConstructors();//包含私有的

        for(Constructor con : constructors) {

            System.out.println(con);

        }

    }

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

反射指定类中的方法:

    //获取类中所有的方法。

    public static void method_1() throws Exception {

        Class clazz = Class.forName("cn.itcast.bean.Person");

        Method[] methods = clazz.getMethods();//获取的是该类中的公有方法和父类中的公有方法。

        methods = clazz.getDeclaredMethods();//获取本类中的方法,包含私有方法。

        for(Method method : methods) {

            System.out.println(method);

        }

    }

    //获取指定方法;

    public static void method_2() throws Exception {

        Class clazz = Class.forName("cn.itcast.bean.Person");

        //获取指定名称的方法。

        Method method = clazz.getMethod("show", int.class,String.class);

        //想要运行指定方法,当然是方法对象最清楚,为了让方法运行,调用方法对象的invoke方法即可,但是方法运行必须要明确所属的对象和具体的实际参数。

        Object obj = clazz.newInstance();

        method.invoke(obj, 39,"hehehe");//执行一个方法

    }

    //想要运行私有方法。

    public static void method_3() throws Exception {

        Class clazz = Class.forName("cn.itcast.bean.Person");

        //想要获取私有方法。必须用getDeclearMethod();

        Method method = clazz.getDeclaredMethod("method", null);

        // 私有方法不能直接访问,因为权限不够。非要访问,可以通过暴力的方式。

        method.setAccessible(true);//一般很少用,因为私有就是隐藏起来,所以尽量不要访问。

    }

    //反射静态方法。

    public static void method_4() throws Exception {

        Class clazz = Class.forName("cn.itcast.bean.Person");

        Method method = clazz.getMethod("function",null);

        method.invoke(null,null);

    }



下文将罗列在实际学习开发中遇到的容易忘记的知识点,虽然简单,但有必要记下来随时查看。

 

概念1:

try-catch-finally块中,finally块在以下几种情况将不会执行。

(1)finally块中发生了异常。

(2)程序所在线程死亡。

(3)在前面的代码中用了System.exit();

(4)关闭了CPU



概念2:

泛型是什么?

答:泛型主要解决安全和代码重用率的问题,它有自动装箱和拆箱的功能。利用与一个不确定的类型来表示任意一个类型,而且泛型通过一个反射机制可以获得这个类的一系列信息,从而提高代码的优越性。



概念3:

Java中的前期绑定和后期绑定

答:前期绑定指在编译时即能确定的类型。

        后期绑定指在编译时不确定类型,在运行时确定类型。


概念4:

面向对象

答:首先:面向对象是一种思想,基于面向过程,可以将复杂问题简单化。其次:它使我们从执行者变为了指挥者,比如说公司现在就是在用面向对象的思维来思考解决问题。公司招人,说明公司在发展壮大,需要一些具备专业编程的人来做事情,从而实现公司的盈利。而我就是那个具备编程能力的对象,所以这就是一种面向对象的思想。

 

 

概念5:

static和final变量:

答:static如果没有初值,默认是0;final变量则必须赋初值。

 

 

概念6:

String,StringBuffer以及StringBuilder之间的关系

(1)线程安全 

StringBuffer 线程安全 
StringBuilder 线程不安全

(2)速度 

一般情况下,速度从快到慢:StringBuilder>StringBuffer>String,这种比较是相对的,不是绝对的。

(3)总结 

如果要操作少量的数据用 = String 
单线程操作字符串缓冲区 下操作大量数据 = StringBuilder 
多线程操作字符串缓冲区 下操作大量数据 = StringBuffer


概念7:

HTTPS和HTTP的区别

答:超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息。HTTP协议以明文方式发送内容,不提 供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此HTTP协议不适合传输一些敏感信息, 比如信用卡号、密码等。为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS。为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。

区别如下:

(1)https协议需要到ca申请证书,一般免费证书很少,需要交费。

(2)http是超文本传输协议,信息是明文传输,https 则是具有安全性ssl加密传输协议。

(3)http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

(4)http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。


 

概念8:

for循环的判断

(1)for(int i=5, j=10; i<10; i++){  }          此语句不会发生编译错误。

(2)int i;

for(i=5, int  j=10; i<10;i++, j++){  }          此语句将发生编译错误。

(3)for(int i=5,j=10; i<10,j>5; i++){  }          此语句将发生编译错误,因为语句2的返回值必须为true或者false。


概念9:

Java数据类型

java数据类型图:                                     ┏数值型━┳━整数型:byte short int long              ┏基本数据类 型<strong>-------</strong>┫        ┗━浮点型:float double              ┃                     ┣字符型:char                                          数据类型------╋                     ┗布尔型:boolean                            ┃                ┏类(class)              ┗引用数据类型━━╋接口(interface)                                ┗数组(array)

引用类型 就是在变量中存储的不是值而是一个内存中的地址的数据类型。也就是说 变量中存储了这个变量的值所在内存中的地址 每次调用这个变量都是引用这个地址而得到真正的值 所以叫引用类型。


概念10:

Java常见的异常类型:

数组角标越界,空指针异常类,算术异常,字符串转换为数字异常,类型不存在异常


概念11:

Java中常见的四种监听方式:

(1)让包含“事件源”的容器对象来担任监听者。即jb.addActionListener(this);this指代一个JFrame对象。

(2)定义内部类来担任监听者。

(3)使用匿名内部类来担任监听者。

 jb.addActionListener(new ActionListener()
        {

            @Override
            public void actionPerformed(ActionEvent e) {
                // TODO Auto-generated method stub                
                System.exit(0);
            }
            
        });

(4)使用外部类来实现监听。



概念12:

Java中为什么会出现空指针异常?

(1)所谓的指针,就是java中的对象的引用。比如String s;这个s就是指针。
(2)所谓的空指针,就是指针的内容为空,比如上面的s,如果令它指向nul了,就是空指针。
(3)所谓的空指针异常,就是一个指针是空指针,你还要去操作它,既然它指向的是空对象,它就不能使用这个对象的方法。比如上面的s假如为null,你还要用s的方法,比如s.wait(1000);那么就会产生空指针异常。



概念13:

Java是解释型还是编译型语言?

答:Java比较特殊,.java文件经过编译之后,再利用解释的方式在运行。

     即,经过编译之后形成了字节码文件(.class)文件,字节码文件可以跨平台运行。其可以在任何一种操作系统中的任何一个JVM上来被解释运行,即JVM是一个字节码解释器。

     所以,Java先编译,再解释。


概念14:  

静态和非静态的区别:

答:(1)首先静态方法中只能有静态成员变量,不能有非静态的普通成员变量。

                        非静态方法中,没有限制。

         (2)其次,在访问方式上。 静态方法只能访问静态方法,不能访问非静态的普通方法。

                        非静态方法则没有限制。

 


概念15:

接口与抽象类的区别:

答:(1)接口里得数据成员必须是静态的,而且必须被初始化。

        (2)接口里得方法必须全部为abstract(即全为抽象方法)。

 

 

16、foreach语句即增强for循环的使用。  

优点:代码整洁,减少了代码量。

缺点:只可以单纯的遍历数组和集合,不可以操作。

举例如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class Demo   
  2. {  
  3.     public static void main(String[] args)   
  4.     {  
  5.         int[] arr=new int[10];  
  6.         int a=0;  
  7.         //使用普通for循环赋值  
  8.         for(int i=0;i<arr.length;i++)  
  9.         {  
  10.               
  11.             arr[i]=a++;  
  12.         }  
  13.   
  14.         //使用高级for循环来读取数组  
  15.         for(int i:arr)  
  16.         {  
  17.             System.out.println(i);  
  18.         }  
  19.           
  20.     }  
  21. }  

 




17、变量的表面类型和实际类型        

      在Java中,只要定义变量就必然要有类型,一个变量可以有两种类型:表面类型和实际类型。表面类型是在定义的时候赋予的类型,实际类型是对象的类型。


  1. interface Person{  
  2.     void run();  
  3.       
  4. }  
  5. class People implements Person{  
  6.   
  7.     @Override  
  8.     public void run() {  
  9.         System.out.println("人在行走。。。");  
  10.           
  11.     }  
  12.       
  13. }  
  14.   
  15. public class  PicClient  
  16. {  
  17.     public static void main(String[] args)  
  18.     {  
  19.         Person p=new People();  
  20.         p.run();  
  21.     }  
  22. }   
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. interface Person{  
  2.     void run();  
  3.       
  4. }  
  5. class People implements Person{  
  6.   
  7.     @Override  
  8.     public void run() {  
  9.         System.out.println("人在行走。。。");  
  10.           
  11.     }  
  12.       
  13. }  
  14.   
  15. public class  PicClient  
  16. {  
  17.     public static void main(String[] args)  
  18.     {  
  19.         Person p=new People();  
  20.         p.run();  
  21.     }  
  22. }   


如代码中所示:主函数中对象p的表面类型是接口Person类型的,但是其实际类型是类People类型的



18、数据结构     

各种数据结构所表示的即和特点说明如下:

(1)线性表表示可重复的无序集合,元素间具有前驱和后继次序关系;不同元素的关键字可以重复,采用序号能够识别关键字重复的数据元素。

(2)排序线性表表示可重复的排序集合,元素按关键字的大小次序排序。

(3)散列表表示不可重复的无序集合,元素关键字不可重复,元素间没有次序,不排序。

(4)二叉排序树表示不可重复的排序集合,元素关键字不重复,元素按关键字升/降序排序。



19、模板方法模式       

模板方法模式就是在模板方法中按照一定的规则和顺序调用基本方法。




20、排序算法总结                  

(1)冒泡排序,直接插入排序以及直接选择排序算法的时间复杂度为O(n2),这些排序算法简单易懂,思路清晰,算法结构为两重循环,共进行n-1趟,每趟排序将一个元素移动到排序后的位置。数据比较和移动在相邻两个元素之间进行,每趟排序与上一趟之间存在较多重复的比较、移动和交换,因此排序效率较低。

(2)希尔排序、快速排序以及堆排序、归并排序效率较高。其共同特点为:与相邻较远的元素进行比较,数据移动距离较远,跳跃式的向目的地前进,避免了许多重复的比较和数据移动。




21、Java字节流和字符流的区别                   

(1)字节流操作字节(8位),字符流操作字符(2个字节,16位)。

(2)字节流不进行close操作,仍然可以输出。

          字符流则必须进行close操作才能输出。因为字符流使用了缓冲区,没有close关闭字符流时,也可以使用flush操作来强行将缓冲区数据输出。




22、js里边是否存在函数重载?                        

答:(1)js里边不存在重载。

        (2)但是可以使用别的方法来模拟函数重载的效果。利用arguments数组,判断接收到几个参数,然后执行相应的操作,实现重载的效果。


23、工厂方法模式和抽象工厂模式的区别:                                  

答:工厂方法模式是一种极端情况的抽象工厂模式,而抽象工厂模式可以看成是工厂方法模式的一种推广。
(1)、其实工厂方法模式是用来创建一个产品的等级结构的,而抽象工厂模式是用来创建多个产品的等级结构的。工厂方法创建一般只有一个方法,创建一种产品。抽象工厂一般有多个方法,创建一系列产品。 
(2)、工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。 
简而言之-> 
工厂方法模式:     一个抽象产品类,可以派生出多个具体产品类。   
                              一个抽象工厂类,可以派生出多个具体工厂类。   
                              每个具体工厂类只能创建一个具体产品类的实例。   
抽象工厂模式:     多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。   
                              一个抽象工厂类,可以派生出多个具体工厂类。   
                              每个具体工厂类可以创建多个具体产品类的实例。 




24、简单工厂模式和工厂方法模式的区别:   

答:简单工厂模式将抽象工厂类去掉,并且将具体工厂类中的方法设置为静态的。这样可以直接通过类来调用方法,即不需要将该工厂创建出来。是一种工厂方法模式的弱化模式,其不符合开闭原则,但是仍然是一种极其好用的设计模式。



25、数组有没有length()方法?String有没有length()方法?  
答:数组没有length()方法,有length 的属性String  有length()方法。JavaScript中,获得字符串的长度是通过length属性得到的,这一点容易和Java混淆。



26、为什么不可以以返回值区分重载方法?   

  如下两个方法:

    void f(){}

    int f(){ return 1;}

只要编译器可以根据语境明确判断出语义,比如在int x = f();中,那么的确可以据此区分重载方法。不过,有时你并不关心方法的返回值,你想要的是方法调用的其他效果(这常被称为“为了副作用而调用”),这时你可能会调用方法而忽略其返回值,所以如果像下面的调用:

    fun();

此时Java如何才能判断调用的是哪一个 f() 呢?别人如何理解这种代码呢?所以,根据方法返回值来区分重载方法是行不通的。

 

以上参考《Java 编程思想》 


 

27、Session的相关方法比较             

答:request.getSession(false)、request.getSession(true)、request.getSession()

       后两个方法效果相同,第一个方法:如果session缓存中(如果cookie不存在),不存在session,那么返回null,而不会创建session对象。也就是说使用第一个方法不一定能够得到一个Session。



28、JavaWeb中配置错误页面的方式:                    


答:(1)通过jsp的page指令的属性errorpage和iserrorpage来设置。

<%@ page errorPage="b.jsp"%>                 <%@page isErrorPage="true"  %>

      (2)在web.xml中配置错误页面。

 <error-page>

    <error-code>404</error-code>

    <location>/error404.jsp</location>

  </error-page>



29、<%@include>和<jsp:include>有什么不同?        


答:前者为JSP静态包含指令,后者为JSP的动作标签之一:动态标签。

前者将两个JSP页面合并起来,生成一个Java文件;   后者分别生成各自的java文件,最后将结果合并输出。




30、JSP相关:

(1)JSP三大指令:

           page指令的常用属性有:import,language, PageEncoding, ContentType, errorPage, isErrorpPage等

           include指令:静态包含。

           taglib指令:导入标签库。


(2)JSP的九大内置对象:

在JSP中无需创建就可以使用的9个对象,它们是:

out(JspWriter):等同与response.getWriter(),用来向客户端发送文本数据;

config(ServletConfig):对应“真身”中的ServletConfig;

page(当前JSP的真身类型):当前JSP页面的“this”,即当前对象;

pageContext(PageContext):页面上下文对象,它是最后一个没讲的域对象;

exception(Throwable):只有在错误页面中可以使用这个对象;

request(HttpServletRequest):即HttpServletRequest类的对象;

response(HttpServletResponse):即HttpServletResponse类的对象;

application(ServletContext):即ServletContext类的对象;

session(HttpSession):即HttpSession类的对象,不是每个JSP页面中都可以使用,如果在某个JSP页面中设置<%@page session=”false”%>,说明这个页面不能使用session。


在这9个对象中有很多是极少会被使用的,例如:config、page、exception基本不会使用。

在这9个对象中有两个对象不是每个JSP页面都可以使用的:exception、session。


(3)JSP的动作标签:

动作标签用来简化java脚本,JavaWeb提供了20个JSP的动作标签,常用的有:

include标签            forward标签             以及子标签param标签用来传递参数。



(4)javaBean规范:

必须有默认的无参构造器,必须提供getter/setter方法。其中属性和成员变量是不同的。可以没有这个成员变量,但是getter/setter后面的这个就叫属性。


(5)EL表达式:

全域查找:${xxx},全域查找名为xxx的属性,如果不存在,输出空字符串,而不是null。
选择一个查找范围:${pageScope.xxx}、${requestScope.xxx}、${sessionScope.xxx}、${applicationScope.xxx},指定域获取属性!


EL表达式11大内置对象:pageScope,requestScope,sessionScope,applicationScope,header,headerValues,param,paramValues,initParam,cookie

                                             pageContext(一个顶N个)

其中前10个都是Map类型的,cookie的键为String类型,Value为Cookie类型。

 

 

 

 

 

31、加载驱动方法             

1.Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");      调用方法 Class.forName
2. DriverManager.registerDriver(new com.mysql.jdbc.Driver());            通过registerDriver方法注册

3.System.setProperty("jdbc.drivers", "com.MySQL.jdbc.Driver");                             通过添加系统的jdbc.drivers属性

 

 

32、接口中的数据类型和方法类型

publicinterfaceIService {

              String NAME="default";

     }   的默认数据类型等价于:public static final  String NAME=“default” ;

接口中的变量默认是public static final 的,方法默认是public abstract 的

 

 

33、equals和==的区别:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class Test{   
  2.     public String name="abc";   
  3.     public static void main(String[] args){   
  4.         Test test=new Test();   
  5.         Test testB=new Test();   
  6.         System.out.println(test.equals(testB)+","+test.name.equals(testB.name));   
  7.     }   
  8. }  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class Test{   
  2.     public String name="abc";   
  3.     public static void main(String[] args){   
  4.         Test test=new Test();   
  5.         Test testB=new Test();   
  6.         System.out.println(test.equals(testB)+","+test.name.equals(testB.name));   
  7.     }   
  8. }  
输出为:false和true
 
解析:equals没重写时候和==一样,比较的是对象的地址,题中new 了两个对象,所以各自地址不一样,使用equals比较为false,但是string类型中的equals方法Java默认重写了,可以比较对象里的值;两个对象指向的同一个string成员变量里的值相同,所以eqauals比较也相同。
即equals方法没重写之前也是比较地址,重写之后可以比较值是否相等。

 

 

 34、Java虚拟机JVM的功能:

(1)通过 ClassLoader 寻找和装载 class 文件
(2)解释字节码成为指令并执行,提供 class 文件的运行环境
(3)进行运行期间垃圾回收
(4)提供与硬件交互的平台

 

 

 

35、会话跟踪技术:

(1)Cookie是Web服务器发送给客户端的一小段信息,客户端请求时,可以读取该信息发送到服务器端
(2)关闭浏览器意味着会话ID丢失,但所有与原会话关联的会话数据仍保留在服务器上,直至会话过期
(3)在禁用Cookie时可以使用URL重写技术跟踪会话

 

 

36、java 类加载器

(1)引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的
(2)扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。
(3)系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类
(4)tomcat 为每个 App 创建一个 Loader,里面保存着此 WebApp 的 ClassLoader。需要加载 WebApp 下的类时,就取出 ClassLoader 来使用

 

 

 

 

37、以下代码输出结果为:(C)

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">int i = 5;  
  2. int j = 10;  
  3. System.out.println(i + ~j);</span>  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">int i = 5;  
  2. int j = 10;  
  3. System.out.println(i + ~j);</span>  

 

A      Compilation error because”~”doesn’t operate on integers
B      -5
C      -6
D     15

解析:很有趣的一个符号,以前没有见过。。。。。。      涉及到补码等知识,公式:~j=-j-1

 

 

 

38、标准输入输出流:

         一般用(System.in)创建InputStream对象,表示从标准输入中获取数据,用(System.out)创建OutputStream对象,表示输出到标准输出设备中。

 

 

 

39、关于运算时的类型问题:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. byte b1=1,b2=2,b3,b6;   
  2. final byte b4=4,b5=6;   
  3. b6=b4+b5;   
  4. b3=(b1+b2);   
  5. System.out.println(b3+b6);  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. byte b1=1,b2=2,b3,b6;   
  2. final byte b4=4,b5=6;   
  3. b6=b4+b5;   
  4. b3=(b1+b2);   
  5. System.out.println(b3+b6);  

 


以上的代码片段叙述正确的为(C)

A       输出结果:13
B      语句:b6=b4+b5编译出错
C      语句:b3=b1+b2编译出错
D      运行期抛出异常

 

解析:

      被final修饰的变量是常量,这里的b6=b4+b5可以看成是b6=10;在编译时就已经变为b6=10了
而b1和b2是byte类型,java中进行计算时候将他们提升为int类型,再进行计算,b1+b2计算后已经是int类型,赋值给b3,b3是byte类型,类型不匹配,编译不会通过,需要进行强制转换。
Java中的byte,short,char进行计算时都会提升为int类型。

 

 

 

40、HashMap解决哈希冲突的方法:

HashMap通过开放地址法解决哈希冲突      

 

解析:

        源码程序中用到了一个重要的内部接口:Map.Entry,每个 Map.Entry 其实就是一个 key-value 对。当系统决定存储 HashMap 中的 key-value 对时,完全没有考虑 Entry 中的 value,仅仅只是根据 key 来计算并决定每个 Entry 的存储位置。Entry是数组,数组中的每个元素上挂这个一条链表。
       链表法就是将相同hash值的对象组织成一个链表放在hash值对应的槽位;开放地址法是通过一个探测算法,当某个槽位已经被占据的情况下继续查找下一个可以使用的槽位。很显然我们使用的不是开放地址法。

 

 

 

 

 41、并发与并行的区别与联系:

       并发:在 操作系统 中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个 处理机 上运行。

       并行:在单处理器中多道程序设计系统中,进程被交替执行,表现出一种并发的外部特性;在多处理器系统中,进程不仅可以交替执行,而且可以重叠执行。在多处理器上的程序才可实现并行处理。从而可知,并行是针对多处理器而言的。并行是同时发生的多个并发事件,具有并发的含义,但并发不一定并行,也亦是说并发事件之间不一定要同一时刻发生。

 

42、Struts1和Struts2的区别:

从action类上分析:
1.Struts1要求Action类继承一个抽象基类。Struts1的一个普遍问题是使用抽象类编程而不是接口。 
2. Struts 2 Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。Struts2提供一个ActionSupport基类去实现常用的接口。Action接口不是必须的,任何有execute标识的POJO对象都可以用作Struts2的Action对象。
从Servlet 依赖分析: 
3. Struts1 Action 依赖于Servlet API ,因为当一个Action被调用时HttpServletRequest 和 HttpServletResponse 被传递给execute方法。 
4. Struts 2 Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts2 Action仍然可以访问初始的request和response。但是,其他的元素减少或者消除了直接访问HttpServetRequest 和 HttpServletResponse的必要性。
从action线程模式分析: 
5. Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能作的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。 
6. Struts2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)

 

 

 

43、非线性数据结构和线性数据结构:           

数据的逻辑结构分为线性结构和非线性结构。
常用的线性结构有:线性表,栈,队列,双队列,数组,串。

常见的非线性结构有:二维数组,多维数组,广义表,树(二叉树等),图。

 

 

 

 44、8中基本数据类型的默认初始值                   

 

 

 45、Java标识符:           

标识符:
    用来表示变量名,方法名,类名,数组名和文件名的有效字符序列。
    也就是说,任何一个变量,常量,方法,对象和类都需要有名字,这些名字就是标识符。
规定如下:
(1)标识符可以由数字、字母、下划线或美元符号等组合而成。
(2)必须以字母、下划线或者美元符号开头,不能以数字开头路。

 

 

 

46、静态static方法竟然可以这么玩。。。

以下程序能正确输出。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class TestClass {  
  2.    private static void testMethod(){  
  3.         System.out.println("testMethod");  
  4.    }  
  5.    public static void main(String[] args) {  
  6.         ((TestClass)null).testMethod();  
  7.    }  
  8. }  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class TestClass {  
  2.    private static void testMethod(){  
  3.         System.out.println("testMethod");  
  4.    }  
  5.    public static void main(String[] args) {  
  6.         ((TestClass)null).testMethod();  
  7.    }  
  8. }  

 

若testMethod( )方法为非静态方法,则报错:空指针异常。。。

 

 

 

 47、Java初始化顺序:               

初始化过程: 
1. 初始化父类中的静态成员变量和静态代码块 ; 
2. 初始化子类中的静态成员变量和静态代码块 ; 
3.初始化父类的普通成员变量和代码块,再执行父类的构造方法;
4.初始化子类的普通成员变量和代码块,再执行子类的构造方法; 


 

 

 

 

48、Servlet的生命周期分为5个阶段:加载、创建、初始化、处理客户请求、卸载。

(1)加载:容器通过类加载器使用servlet类对应的文件加载servlet
(2)创建:通过调用servlet构造函数创建一个servlet对象
(3)初始化:调用init方法初始化
(4)处理客户请求:每当有一个客户请求,容器会创建一个线程来处理客户请求
(5)卸载:调用destroy方法让servlet自己释放其占用的资源

 

 

 49、Java中的异常相关概念:         

throw用于在方法内抛出异常对象。

throws关键字用于在方法上抛出异常。

try是用于检测被包住的语句块是否出现异常,如果有异常,则抛出异常,并执行catch语句。

finally语句块是不管有没有出现异常都要执行的内容。

 


 

50、静态内部类和非静态内部类创建对象的区别:   

 

 

 

 

 

下文将罗列在实际学习开发中遇到的容易忘记的知识点,虽然简单,但有必要记下来随时查看。

 

概念1:

try-catch-finally块中,finally块在以下几种情况将不会执行。

(1)finally块中发生了异常。

(2)程序所在线程死亡。

(3)在前面的代码中用了System.exit();

(4)关闭了CPU



概念2:

泛型是什么?

答:泛型主要解决安全和代码重用率的问题,它有自动装箱和拆箱的功能。利用与一个不确定的类型来表示任意一个类型,而且泛型通过一个反射机制可以获得这个类的一系列信息,从而提高代码的优越性。



概念3:

Java中的前期绑定和后期绑定

答:前期绑定指在编译时即能确定的类型。

        后期绑定指在编译时不确定类型,在运行时确定类型。


概念4:

面向对象

答:首先:面向对象是一种思想,基于面向过程,可以将复杂问题简单化。其次:它使我们从执行者变为了指挥者,比如说公司现在就是在用面向对象的思维来思考解决问题。公司招人,说明公司在发展壮大,需要一些具备专业编程的人来做事情,从而实现公司的盈利。而我就是那个具备编程能力的对象,所以这就是一种面向对象的思想。

 

 

概念5:

static和final变量:

答:static如果没有初值,默认是0;final变量则必须赋初值。

 

 

概念6:

String,StringBuffer以及StringBuilder之间的关系

(1)线程安全 

StringBuffer 线程安全 
StringBuilder 线程不安全

(2)速度 

一般情况下,速度从快到慢:StringBuilder>StringBuffer>String,这种比较是相对的,不是绝对的。

(3)总结 

如果要操作少量的数据用 = String 
单线程操作字符串缓冲区 下操作大量数据 = StringBuilder 
多线程操作字符串缓冲区 下操作大量数据 = StringBuffer


概念7:

HTTPS和HTTP的区别

答:超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息。HTTP协议以明文方式发送内容,不提 供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此HTTP协议不适合传输一些敏感信息, 比如信用卡号、密码等。为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS。为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。

区别如下:

(1)https协议需要到ca申请证书,一般免费证书很少,需要交费。

(2)http是超文本传输协议,信息是明文传输,https 则是具有安全性ssl加密传输协议。

(3)http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

(4)http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。


 

概念8:

for循环的判断

(1)for(int i=5, j=10; i<10; i++){  }          此语句不会发生编译错误。

(2)int i;

for(i=5, int  j=10; i<10;i++, j++){  }          此语句将发生编译错误。

(3)for(int i=5,j=10; i<10,j>5; i++){  }          此语句将发生编译错误,因为语句2的返回值必须为true或者false。


概念9:

Java数据类型

java数据类型图:                                     ┏数值型━┳━整数型:byte short int long              ┏基本数据类 型<strong>-------</strong>┫        ┗━浮点型:float double              ┃                     ┣字符型:char                                          数据类型------╋                     ┗布尔型:boolean                            ┃                ┏类(class)              ┗引用数据类型━━╋接口(interface)                                ┗数组(array)

引用类型 就是在变量中存储的不是值而是一个内存中的地址的数据类型。也就是说 变量中存储了这个变量的值所在内存中的地址 每次调用这个变量都是引用这个地址而得到真正的值 所以叫引用类型。


概念10:

Java常见的异常类型:

数组角标越界,空指针异常类,算术异常,字符串转换为数字异常,类型不存在异常


概念11:

Java中常见的四种监听方式:

(1)让包含“事件源”的容器对象来担任监听者。即jb.addActionListener(this);this指代一个JFrame对象。

(2)定义内部类来担任监听者。

(3)使用匿名内部类来担任监听者。

 jb.addActionListener(new ActionListener()
        {

            @Override
            public void actionPerformed(ActionEvent e) {
                // TODO Auto-generated method stub                
                System.exit(0);
            }
            
        });

(4)使用外部类来实现监听。



概念12:

Java中为什么会出现空指针异常?

(1)所谓的指针,就是java中的对象的引用。比如String s;这个s就是指针。
(2)所谓的空指针,就是指针的内容为空,比如上面的s,如果令它指向nul了,就是空指针。
(3)所谓的空指针异常,就是一个指针是空指针,你还要去操作它,既然它指向的是空对象,它就不能使用这个对象的方法。比如上面的s假如为null,你还要用s的方法,比如s.wait(1000);那么就会产生空指针异常。



概念13:

Java是解释型还是编译型语言?

答:Java比较特殊,.java文件经过编译之后,再利用解释的方式在运行。

     即,经过编译之后形成了字节码文件(.class)文件,字节码文件可以跨平台运行。其可以在任何一种操作系统中的任何一个JVM上来被解释运行,即JVM是一个字节码解释器。

     所以,Java先编译,再解释。


概念14:  

静态和非静态的区别:

答:(1)首先静态方法中只能有静态成员变量,不能有非静态的普通成员变量。

                        非静态方法中,没有限制。

         (2)其次,在访问方式上。 静态方法只能访问静态方法,不能访问非静态的普通方法。

                        非静态方法则没有限制。

 


概念15:

接口与抽象类的区别:

答:(1)接口里得数据成员必须是静态的,而且必须被初始化。

        (2)接口里得方法必须全部为abstract(即全为抽象方法)。

 

 

16、foreach语句即增强for循环的使用。  

优点:代码整洁,减少了代码量。

缺点:只可以单纯的遍历数组和集合,不可以操作。

举例如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class Demo   
  2. {  
  3.     public static void main(String[] args)   
  4.     {  
  5.         int[] arr=new int[10];  
  6.         int a=0;  
  7.         //使用普通for循环赋值  
  8.         for(int i=0;i<arr.length;i++)  
  9.         {  
  10.               
  11.             arr[i]=a++;  
  12.         }  
  13.   
  14.         //使用高级for循环来读取数组  
  15.         for(int i:arr)  
  16.         {  
  17.             System.out.println(i);  
  18.         }  
  19.           
  20.     }  
  21. }  

 




17、变量的表面类型和实际类型        

      在Java中,只要定义变量就必然要有类型,一个变量可以有两种类型:表面类型和实际类型。表面类型是在定义的时候赋予的类型,实际类型是对象的类型。


  1. interface Person{  
  2.     void run();  
  3.       
  4. }  
  5. class People implements Person{  
  6.   
  7.     @Override  
  8.     public void run() {  
  9.         System.out.println("人在行走。。。");  
  10.           
  11.     }  
  12.       
  13. }  
  14.   
  15. public class  PicClient  
  16. {  
  17.     public static void main(String[] args)  
  18.     {  
  19.         Person p=new People();  
  20.         p.run();  
  21.     }  
  22. }   
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. interface Person{  
  2.     void run();  
  3.       
  4. }  
  5. class People implements Person{  
  6.   
  7.     @Override  
  8.     public void run() {  
  9.         System.out.println("人在行走。。。");  
  10.           
  11.     }  
  12.       
  13. }  
  14.   
  15. public class  PicClient  
  16. {  
  17.     public static void main(String[] args)  
  18.     {  
  19.         Person p=new People();  
  20.         p.run();  
  21.     }  
  22. }   


如代码中所示:主函数中对象p的表面类型是接口Person类型的,但是其实际类型是类People类型的



18、数据结构     

各种数据结构所表示的即和特点说明如下:

(1)线性表表示可重复的无序集合,元素间具有前驱和后继次序关系;不同元素的关键字可以重复,采用序号能够识别关键字重复的数据元素。

(2)排序线性表表示可重复的排序集合,元素按关键字的大小次序排序。

(3)散列表表示不可重复的无序集合,元素关键字不可重复,元素间没有次序,不排序。

(4)二叉排序树表示不可重复的排序集合,元素关键字不重复,元素按关键字升/降序排序。



19、模板方法模式       

模板方法模式就是在模板方法中按照一定的规则和顺序调用基本方法。




20、排序算法总结                  

(1)冒泡排序,直接插入排序以及直接选择排序算法的时间复杂度为O(n2),这些排序算法简单易懂,思路清晰,算法结构为两重循环,共进行n-1趟,每趟排序将一个元素移动到排序后的位置。数据比较和移动在相邻两个元素之间进行,每趟排序与上一趟之间存在较多重复的比较、移动和交换,因此排序效率较低。

(2)希尔排序、快速排序以及堆排序、归并排序效率较高。其共同特点为:与相邻较远的元素进行比较,数据移动距离较远,跳跃式的向目的地前进,避免了许多重复的比较和数据移动。




21、Java字节流和字符流的区别                   

(1)字节流操作字节(8位),字符流操作字符(2个字节,16位)。

(2)字节流不进行close操作,仍然可以输出。

          字符流则必须进行close操作才能输出。因为字符流使用了缓冲区,没有close关闭字符流时,也可以使用flush操作来强行将缓冲区数据输出。




22、js里边是否存在函数重载?                        

答:(1)js里边不存在重载。

        (2)但是可以使用别的方法来模拟函数重载的效果。利用arguments数组,判断接收到几个参数,然后执行相应的操作,实现重载的效果。


23、工厂方法模式和抽象工厂模式的区别:                                  

答:工厂方法模式是一种极端情况的抽象工厂模式,而抽象工厂模式可以看成是工厂方法模式的一种推广。
(1)、其实工厂方法模式是用来创建一个产品的等级结构的,而抽象工厂模式是用来创建多个产品的等级结构的。工厂方法创建一般只有一个方法,创建一种产品。抽象工厂一般有多个方法,创建一系列产品。 
(2)、工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。 
简而言之-> 
工厂方法模式:     一个抽象产品类,可以派生出多个具体产品类。   
                              一个抽象工厂类,可以派生出多个具体工厂类。   
                              每个具体工厂类只能创建一个具体产品类的实例。   
抽象工厂模式:     多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。   
                              一个抽象工厂类,可以派生出多个具体工厂类。   
                              每个具体工厂类可以创建多个具体产品类的实例。 




24、简单工厂模式和工厂方法模式的区别:   

答:简单工厂模式将抽象工厂类去掉,并且将具体工厂类中的方法设置为静态的。这样可以直接通过类来调用方法,即不需要将该工厂创建出来。是一种工厂方法模式的弱化模式,其不符合开闭原则,但是仍然是一种极其好用的设计模式。



25、数组有没有length()方法?String有没有length()方法?  
答:数组没有length()方法,有length 的属性String  有length()方法。JavaScript中,获得字符串的长度是通过length属性得到的,这一点容易和Java混淆。



26、为什么不可以以返回值区分重载方法?   

  如下两个方法:

    void f(){}

    int f(){ return 1;}

只要编译器可以根据语境明确判断出语义,比如在int x = f();中,那么的确可以据此区分重载方法。不过,有时你并不关心方法的返回值,你想要的是方法调用的其他效果(这常被称为“为了副作用而调用”),这时你可能会调用方法而忽略其返回值,所以如果像下面的调用:

    fun();

此时Java如何才能判断调用的是哪一个 f() 呢?别人如何理解这种代码呢?所以,根据方法返回值来区分重载方法是行不通的。

 

以上参考《Java 编程思想》 


 

27、Session的相关方法比较             

答:request.getSession(false)、request.getSession(true)、request.getSession()

       后两个方法效果相同,第一个方法:如果session缓存中(如果cookie不存在),不存在session,那么返回null,而不会创建session对象。也就是说使用第一个方法不一定能够得到一个Session。



28、JavaWeb中配置错误页面的方式:                    


答:(1)通过jsp的page指令的属性errorpage和iserrorpage来设置。

<%@ page errorPage="b.jsp"%>                 <%@page isErrorPage="true"  %>

      (2)在web.xml中配置错误页面。

 <error-page>

    <error-code>404</error-code>

    <location>/error404.jsp</location>

  </error-page>



29、<%@include>和<jsp:include>有什么不同?        


答:前者为JSP静态包含指令,后者为JSP的动作标签之一:动态标签。

前者将两个JSP页面合并起来,生成一个Java文件;   后者分别生成各自的java文件,最后将结果合并输出。




30、JSP相关:

(1)JSP三大指令:

           page指令的常用属性有:import,language, PageEncoding, ContentType, errorPage, isErrorpPage等

           include指令:静态包含。

           taglib指令:导入标签库。


(2)JSP的九大内置对象:

在JSP中无需创建就可以使用的9个对象,它们是:

out(JspWriter):等同与response.getWriter(),用来向客户端发送文本数据;

config(ServletConfig):对应“真身”中的ServletConfig;

page(当前JSP的真身类型):当前JSP页面的“this”,即当前对象;

pageContext(PageContext):页面上下文对象,它是最后一个没讲的域对象;

exception(Throwable):只有在错误页面中可以使用这个对象;

request(HttpServletRequest):即HttpServletRequest类的对象;

response(HttpServletResponse):即HttpServletResponse类的对象;

application(ServletContext):即ServletContext类的对象;

session(HttpSession):即HttpSession类的对象,不是每个JSP页面中都可以使用,如果在某个JSP页面中设置<%@page session=”false”%>,说明这个页面不能使用session。


在这9个对象中有很多是极少会被使用的,例如:config、page、exception基本不会使用。

在这9个对象中有两个对象不是每个JSP页面都可以使用的:exception、session。


(3)JSP的动作标签:

动作标签用来简化java脚本,JavaWeb提供了20个JSP的动作标签,常用的有:

include标签            forward标签             以及子标签param标签用来传递参数。



(4)javaBean规范:

必须有默认的无参构造器,必须提供getter/setter方法。其中属性和成员变量是不同的。可以没有这个成员变量,但是getter/setter后面的这个就叫属性。


(5)EL表达式:

全域查找:${xxx},全域查找名为xxx的属性,如果不存在,输出空字符串,而不是null。
选择一个查找范围:${pageScope.xxx}、${requestScope.xxx}、${sessionScope.xxx}、${applicationScope.xxx},指定域获取属性!


EL表达式11大内置对象:pageScope,requestScope,sessionScope,applicationScope,header,headerValues,param,paramValues,initParam,cookie

                                             pageContext(一个顶N个)

其中前10个都是Map类型的,cookie的键为String类型,Value为Cookie类型。

 

 

 

 

 

31、加载驱动方法             

1.Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");      调用方法 Class.forName
2. DriverManager.registerDriver(new com.mysql.jdbc.Driver());            通过registerDriver方法注册

3.System.setProperty("jdbc.drivers", "com.MySQL.jdbc.Driver");                             通过添加系统的jdbc.drivers属性

 

 

32、接口中的数据类型和方法类型

publicinterfaceIService {

              String NAME="default";

     }   的默认数据类型等价于:public static final  String NAME=“default” ;

接口中的变量默认是public static final 的,方法默认是public abstract 的

 

 

33、equals和==的区别:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class Test{   
  2.     public String name="abc";   
  3.     public static void main(String[] args){   
  4.         Test test=new Test();   
  5.         Test testB=new Test();   
  6.         System.out.println(test.equals(testB)+","+test.name.equals(testB.name));   
  7.     }   
  8. }  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class Test{   
  2.     public String name="abc";   
  3.     public static void main(String[] args){   
  4.         Test test=new Test();   
  5.         Test testB=new Test();   
  6.         System.out.println(test.equals(testB)+","+test.name.equals(testB.name));   
  7.     }   
  8. }  
输出为:false和true
 
解析:equals没重写时候和==一样,比较的是对象的地址,题中new 了两个对象,所以各自地址不一样,使用equals比较为false,但是string类型中的equals方法Java默认重写了,可以比较对象里的值;两个对象指向的同一个string成员变量里的值相同,所以eqauals比较也相同。
即equals方法没重写之前也是比较地址,重写之后可以比较值是否相等。

 

 

 34、Java虚拟机JVM的功能:

(1)通过 ClassLoader 寻找和装载 class 文件
(2)解释字节码成为指令并执行,提供 class 文件的运行环境
(3)进行运行期间垃圾回收
(4)提供与硬件交互的平台

 

 

 

35、会话跟踪技术:

(1)Cookie是Web服务器发送给客户端的一小段信息,客户端请求时,可以读取该信息发送到服务器端
(2)关闭浏览器意味着会话ID丢失,但所有与原会话关联的会话数据仍保留在服务器上,直至会话过期
(3)在禁用Cookie时可以使用URL重写技术跟踪会话

 

 

36、java 类加载器

(1)引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的
(2)扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。
(3)系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类
(4)tomcat 为每个 App 创建一个 Loader,里面保存着此 WebApp 的 ClassLoader。需要加载 WebApp 下的类时,就取出 ClassLoader 来使用

 

 

 

 

37、以下代码输出结果为:(C)

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">int i = 5;  
  2. int j = 10;  
  3. System.out.println(i + ~j);</span>  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">int i = 5;  
  2. int j = 10;  
  3. System.out.println(i + ~j);</span>  

 

A      Compilation error because”~”doesn’t operate on integers
B      -5
C      -6
D     15

解析:很有趣的一个符号,以前没有见过。。。。。。      涉及到补码等知识,公式:~j=-j-1

 

 

 

38、标准输入输出流:

         一般用(System.in)创建InputStream对象,表示从标准输入中获取数据,用(System.out)创建OutputStream对象,表示输出到标准输出设备中。

 

 

 

39、关于运算时的类型问题:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. byte b1=1,b2=2,b3,b6;   
  2. final byte b4=4,b5=6;   
  3. b6=b4+b5;   
  4. b3=(b1+b2);   
  5. System.out.println(b3+b6);  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. byte b1=1,b2=2,b3,b6;   
  2. final byte b4=4,b5=6;   
  3. b6=b4+b5;   
  4. b3=(b1+b2);   
  5. System.out.println(b3+b6);  

 


以上的代码片段叙述正确的为(C)

A       输出结果:13
B      语句:b6=b4+b5编译出错
C      语句:b3=b1+b2编译出错
D      运行期抛出异常

 

解析:

      被final修饰的变量是常量,这里的b6=b4+b5可以看成是b6=10;在编译时就已经变为b6=10了
而b1和b2是byte类型,java中进行计算时候将他们提升为int类型,再进行计算,b1+b2计算后已经是int类型,赋值给b3,b3是byte类型,类型不匹配,编译不会通过,需要进行强制转换。
Java中的byte,short,char进行计算时都会提升为int类型。

 

 

 

40、HashMap解决哈希冲突的方法:

HashMap通过开放地址法解决哈希冲突      

 

解析:

        源码程序中用到了一个重要的内部接口:Map.Entry,每个 Map.Entry 其实就是一个 key-value 对。当系统决定存储 HashMap 中的 key-value 对时,完全没有考虑 Entry 中的 value,仅仅只是根据 key 来计算并决定每个 Entry 的存储位置。Entry是数组,数组中的每个元素上挂这个一条链表。
       链表法就是将相同hash值的对象组织成一个链表放在hash值对应的槽位;开放地址法是通过一个探测算法,当某个槽位已经被占据的情况下继续查找下一个可以使用的槽位。很显然我们使用的不是开放地址法。

 

 

 

 

 41、并发与并行的区别与联系:

       并发:在 操作系统 中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个 处理机 上运行。

       并行:在单处理器中多道程序设计系统中,进程被交替执行,表现出一种并发的外部特性;在多处理器系统中,进程不仅可以交替执行,而且可以重叠执行。在多处理器上的程序才可实现并行处理。从而可知,并行是针对多处理器而言的。并行是同时发生的多个并发事件,具有并发的含义,但并发不一定并行,也亦是说并发事件之间不一定要同一时刻发生。

 

42、Struts1和Struts2的区别:

从action类上分析:
1.Struts1要求Action类继承一个抽象基类。Struts1的一个普遍问题是使用抽象类编程而不是接口。 
2. Struts 2 Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。Struts2提供一个ActionSupport基类去实现常用的接口。Action接口不是必须的,任何有execute标识的POJO对象都可以用作Struts2的Action对象。
从Servlet 依赖分析: 
3. Struts1 Action 依赖于Servlet API ,因为当一个Action被调用时HttpServletRequest 和 HttpServletResponse 被传递给execute方法。 
4. Struts 2 Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts2 Action仍然可以访问初始的request和response。但是,其他的元素减少或者消除了直接访问HttpServetRequest 和 HttpServletResponse的必要性。
从action线程模式分析: 
5. Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能作的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。 
6. Struts2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)

 

 

 

43、非线性数据结构和线性数据结构:           

数据的逻辑结构分为线性结构和非线性结构。
常用的线性结构有:线性表,栈,队列,双队列,数组,串。

常见的非线性结构有:二维数组,多维数组,广义表,树(二叉树等),图。

 

 

 

 44、8中基本数据类型的默认初始值                   

 

 

 45、Java标识符:           

标识符:
    用来表示变量名,方法名,类名,数组名和文件名的有效字符序列。
    也就是说,任何一个变量,常量,方法,对象和类都需要有名字,这些名字就是标识符。
规定如下:
(1)标识符可以由数字、字母、下划线或美元符号等组合而成。
(2)必须以字母、下划线或者美元符号开头,不能以数字开头路。

 

 

 

46、静态static方法竟然可以这么玩。。。

以下程序能正确输出。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class TestClass {  
  2.    private static void testMethod(){  
  3.         System.out.println("testMethod");  
  4.    }  
  5.    public static void main(String[] args) {  
  6.         ((TestClass)null).testMethod();  
  7.    }  
  8. }  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class TestClass {  
  2.    private static void testMethod(){  
  3.         System.out.println("testMethod");  
  4.    }  
  5.    public static void main(String[] args) {  
  6.         ((TestClass)null).testMethod();  
  7.    }  
  8. }  

 

若testMethod( )方法为非静态方法,则报错:空指针异常。。。

 

 

 

 47、Java初始化顺序:               

初始化过程: 
1. 初始化父类中的静态成员变量和静态代码块 ; 
2. 初始化子类中的静态成员变量和静态代码块 ; 
3.初始化父类的普通成员变量和代码块,再执行父类的构造方法;
4.初始化子类的普通成员变量和代码块,再执行子类的构造方法; 


 

 

 

 

48、Servlet的生命周期分为5个阶段:加载、创建、初始化、处理客户请求、卸载。

(1)加载:容器通过类加载器使用servlet类对应的文件加载servlet
(2)创建:通过调用servlet构造函数创建一个servlet对象
(3)初始化:调用init方法初始化
(4)处理客户请求:每当有一个客户请求,容器会创建一个线程来处理客户请求
(5)卸载:调用destroy方法让servlet自己释放其占用的资源

 

 

 49、Java中的异常相关概念:         

throw用于在方法内抛出异常对象。

throws关键字用于在方法上抛出异常。

try是用于检测被包住的语句块是否出现异常,如果有异常,则抛出异常,并执行catch语句。

finally语句块是不管有没有出现异常都要执行的内容。

 


 

50、静态内部类和非静态内部类创建对象的区别:   

 

 

 

 

 

0 0
原创粉丝点击