Java基础部分

来源:互联网 发布:java 参数 ... 传参 编辑:程序博客网 时间:2024/06/16 08:28

基础部分的一些总结,基本都是在学习的时候遇到的一些问题,有些是来自于其他人的博客,有些是自己总结的,如果有侵权请联系我,并表示歉意。

---------------------------------------------------------------------------------------------------------------------
 java中代码的注释和快捷键

添加必要的注释,对一个有责任心、有道德模范的前端必须具备的好习惯,

可以大大提高代码的可维护性、可读性。

java代码注释快捷键:文本注释ctrl+shift+/
            单行注释ctrl+/
            多行注释ctrl+shift+c
            文档注释alt +shift + j
首先熟悉一下html、css、js的注释的写法:
1、HTML注释语法:

<!--注释的内容-->

2、css注释语法

/* 注释内容 */
/* ----------文字样式开始---------- */

3、javaScript注释

//注释内容
/*注释内容*/

接下来是对注释在这几种代码中使用的位置,如何写注释进行总结一下。(根据个人的习惯可能不一样)
1、html注释
使用的位置:
1)一般会使用在一些主要节点标签结束的后边,如:

<div class="wrap"> <div class="main"> ...</div><!--main end--> <div><!--wrap end-->

2)使用在一些循环的结束的后边,如:

<ul class="list">     <li>111111</li>     <li>222222</li>     <li>333333</li> </ul><!--list loop-->

 

2、css注释
一般会使用在定义某个模块样式的上边,说明这段样式是作用于哪段模块,如:

/*通用 - 评论*/ .comment{...} /*相册*/ .photo{...} /*分享*/ .share{...} /*投票*/ .vote{...}

3、javascript注释
一般将注释添加在某段功能函数的上边,说明函数的功能、作者、作者信息、修改时间。

//======================================================== //      截断字符 //      steepvi //      rtx:******* //      2010-10-14 //========================================================

最后,注释也是字符也是会有流量产生。因此当页面发布到正式地址的时候,最好加上一步优化流程。
ctrl + /

ctrl + shift + c

以下是myeclipse中的所有快捷键列表:
Ctrl+1 快速修复(最经典的快捷键,就不用多说了)
Ctrl+D: 删除当前行
Ctrl+Alt+↓ 复制当前行到下一行(复制增加)
Ctrl+Alt+↑ 复制当前行到上一行(复制增加)
Alt+↓ 当前行和下面一行交互位置(特别实用,可以省去先剪切,再粘贴了)
Alt+↑ 当前行和上面一行交互位置(同上)
Alt+← 前一个编辑的页面
Alt+→ 下一个编辑的页面(当然是针对上面那条来说了)
Alt+Enter 显示当前选择资源(工程,or 文件 or文件)的属性
Shift+Enter 在当前行的下一行插入空行(这时鼠标可以在当前行的任一位置,不一定是最后)
Shift+Ctrl+Enter 在当前行插入空行(原理同上条)
Ctrl+Q 定位到最后编辑的地方
Ctrl+L 定位在某行 (对于程序超过100的人就有福音了)
Ctrl+M 最大化当前的Edit或View (再按则反之)
Ctrl+/ 注释当前行,再按则取消注释
Ctrl+O 快速显示 OutLine
Ctrl+T 快速显示当前类的继承结构
Ctrl+W 关闭当前Editer
Ctrl+K 参照选中的Word快速定位到下一个
Ctrl+E 快速显示当前Editer的下拉列表(如果当前页面没有显示的用黑体表示)
Ctrl+/(小键盘) 折叠当前类中的所有代码
Ctrl+×(小键盘) 展开当前类中的所有代码
Ctrl+Space 代码助手完成一些代码的插入(但一般和输入法有冲突,可以修改输入法的热键,也可以暂用Alt+/来代替)
Ctrl+Shift+E 显示管理当前打开的所有的View的管理器(可以选择关闭,激活等操作)
Ctrl+J 正向增量查找(按下Ctrl+J后,你所输入的每个字母编辑器都提供快速匹配定位到某个单词,如果没有,则在stutes line中显示没有找到了,查一个单词时,特别实用,这个功能Idea两年前就有了)
Ctrl+Shift+J 反向增量查找(和上条相同,只不过是从后往前查)
Ctrl+Shift+F4 关闭所有打开的Editer
Ctrl+Shift+X 把当前选中的文本全部变味小写
Ctrl+Shift+Y 把当前选中的文本全部变为小写
Ctrl+Shift+F 格式化当前代码
Ctrl+Shift+P 定位到对于的匹配符(譬如{}) (从前面定位后面时,光标要在匹配符里面,后面到前面,则反之)

下面的快捷键是重构里面常用的,本人就自己喜欢且常用的整理一下(注:一般重构的快捷键都是Alt+Shift开头的了)
Alt+Shift+R 重命名 (是我自己最爱用的一个了,尤其是变量和类的Rename,比手工方法能节省很多劳动力)
Alt+Shift+M 抽取方法 (这是重构里面最常用的方法之一了,尤其是对一大堆泥团代码有用)
Alt+Shift+C 修改函数结构(比较实用,有N个函数调用了这个方法,修改一次搞定)
Alt+Shift+L 抽取本地变量( 可以直接把一些魔法数字和字符串抽取成一个变量,尤其是多处调用的时候)
Alt+Shift+F 把Class中的local变量变为field变量 (比较实用的功能)
Alt+Shift+I 合并变量(可能这样说有点不妥Inline)
Alt+Shift+V 移动函数和变量(不怎么常用)
Alt+Shift+Z 重构的后悔药(Undo)


查看
作用域 功能 快捷键
全局 放大 Ctrl+=
全局 缩小 Ctrl+-

窗口
作用域 功能 快捷键
全局 激活编辑器 F12
全局 切换编辑器 Ctrl+Shift+W
全局 上一个编辑器 Ctrl+Shift+F6
全局 上一个视图 Ctrl+Shift+F7
全局 上一个透视图 Ctrl+Shift+F8
全局 下一个编辑器 Ctrl+F6
全局 下一个视图 Ctrl+F7
全局 下一个透视图 Ctrl+F8
文本编辑器 显示标尺上下文菜单 Ctrl+W
全局 显示视图菜单 Ctrl+F10
全局 显示系统菜单 Alt+-

文件
作用域 功能 快捷键
全局 保存 Ctrl+X
Ctrl+S
全局 打印 Ctrl+P
全局 关闭 Ctrl+F4
全局 全部保存 Ctrl+Shift+S
全局 全部关闭 Ctrl+Shift+F4
全局 属性 Alt+Enter
全局 新建 Ctrl+N

源代码
作用域 功能 快捷键
Java编辑器 格式化 Ctrl+Shift+F
Java编辑器 取消注释 Ctrl+\
Java编辑器 注释 Ctrl+/
Java编辑器 添加导入 Ctrl+Shift+M
Java编辑器 组织导入 Ctrl+Shift+O
Java编辑器 使用try/catch块来包围 未设置,太常用了,所以在这里列出,建议自己设置。
也可以使用Ctrl+1自动修正。

运行
作用域 功能 快捷键
全局 单步返回 F7
全局 单步跳过 F6
全局 单步跳入 F5
全局 单步跳入选择 Ctrl+F5
全局 调试上次启动 F11
全局 继续 F8
全局 使用过滤器单步执行 Shift+F5
全局 添加/去除断点 Ctrl+Shift+B
全局 显示 Ctrl+D
全局 运行上次启动 Ctrl+F11
全局 运行至行 Ctrl+R
全局 执行 Ctrl+U

重构
作用域 功能 快捷键
全局 撤销重构 Alt+Shift+Z
全局 抽取方法 Alt+Shift+M
全局 抽取局部变量 Alt+Shift+L
全局 内联 Alt+Shift+I
全局 移动 Alt+Shift+V
全局 重命名 Alt+Shift+R
全局 重做 Alt+Shift+Y
/**
 * @author sram
 * @category 展示主方法
 */
public class MyTest {
    //单行注释: 这个方法是主方法是所有Java代码的入口
    public static void main(String[] args) {
        /*
         * 主方法的方法体
         * 主方法的执行代码块为:System.out.println("HelloWorld");
         * 主要负责输出字符串"HelloWorld"
         */
        System.out.println("HelloWorld");
    }
}
-----------------------------------------------------------------------------------------------------------------------------------------
java用编译器编译成字节码,字节码用解释器运行,同一字节码可以在不同的操作系统下被不同的解释器运行,从而实现Java的跨平台性也就可移植性.

补充最重要的一点:因为字节码,即class文件是按规定标准排列的二进制文件,所以不同操作系统的java编译器都会编译出一致的字节码文件。
-----------------------------------------------------------------------------------------------------------------------------------------
ASCII码:一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。一个二进制数字序列,在计算机中作为一个数字单元,一般为8位二进制数,换算为十进制。最小值0,最大值255。如一个ASCII码就是一个字节。
UTF-8编码:一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。
Unicode编码:一个英文等于两个字节,一个中文(含繁体)等于两个字节。
符号:英文标点占一个字节,中文标点占两个字节。举例:英文句号“.”占1个字节的大小,中文句号“。”占2个字节的大小。

不同数量级间
数据存储是以10进制表示,数据传输是以2进制表示的,所以1KB不等于1000B。
1KB=1024B;1MB=1024KB=1024×1024B。其中1024=210。
1B(byte,字节)= 8 bit(见下文);
1KB(Kibibyte,千字节)=1024B= 2^10 B;
1MB(Mebibyte,兆字节,百万字节,简称“兆”)=1024KB= 2^20 B;
1GB(Gigabyte,吉字节,十亿字节,又称“千兆”)=1024MB= 2^30 B;
1TB(Terabyte,万亿字节,太字节)=1024GB= 2^40 B;
1PB(Petabyte,千万亿字节,拍字节)=1024TB= 2^50 B;
1EB(Exabyte,百亿亿字节,艾字节)=1024PB= 2^60 B;
1ZB(Zettabyte,十万亿亿字节,泽字节)= 1024EB= 2^70 B;
1YB(Yottabyte,一亿亿亿字节,尧字节)= 1024ZB= 2^80 B;
1BB(Brontobyte,一千亿亿亿字节)= 1024YB= 2^90 B;
1NB(NonaByte,一百万亿亿亿字节) = 1024 BB = 2^100 B;
1DB(DoggaByte,十亿亿亿亿字节) = 1024 NB = 2^110 B;[1]  

数据类型         储存空间大小(单位:字节)    范围
Byte(字节型)        1            0 - 255
Boolean(布尔型/逻辑型)    2            True 或 False
Integer(整数型)        2            -32,768 ~ 32767
Long(长整形)        4            -2,147,483,648 ~ 2,147,483,647
Single(单精度浮点型)    4            负数范围:-3.402823E38 ~ -1.401298E-45 正数范围:1.401298E-45 ~ 3.402823E38
Double(双精度浮点型)    8            负数范围:-1.797,693,134,862,32E308 ~-4.940,656,458,412,47E-324
                        正数范围:4.940,656,458,412,47E-324 ~1.797,693,134,862,32E308
Currency(变比整形 / 货币类型) 8            -922,337,203,685,477.5808 ~922,337,203,685,477.5807
Decimal(十进制型)    14            没有小数点时:+/-79,228,162,514,264,337,593,543,950,335
                        有小数点时:+/-7.922,816,251,426,433,759,354,395,033,5
                        最小的非零值:+/-0.000,000,000,000,000,000,000,000,000,1
Date(时间日期型)    8            100 年 1 月 1 日 ~ 9999 年 12 月 31 日
Object(对象型)        4            任何 Object 引用
String (变长)        10            长度从 0 到 大约 20 亿
String (定长)        10            长度从 1 到大约 65,400
Variant (数字)        16            任何数字值,最大可达 Double 的范围
Variant (字符)        22            与字符串长度,变长 String 有相同的范围


B与bit
数据存储是以“字节”(Byte)为单位,数据传输是以大多是以“位”(bit,又名“比特”)为单位,一个位就代表一个0或1(即二进制),每8个位(bit,简写为b)组成一个字节(Byte,简写为B),是最小一级的信息单位。
B与iB
1KB(Kibibyte)=1024byte
1KiB(Kilobyte)=1000byte
1MB(Mebibyte)=1048576byte
1MiB(Megabyte)=1000000byte
硬盘生产商是以GiB(十进制,即10的3次方=1000,如1MiB=1000KB)计算的,而电脑(操作系统)是以GB(2进制,即2的10次方, 如1MB=1024KB)计算的,但是国内用户一般理解为1MiB=1M=1024 KB, 所以为了便于中文化的理解,翻译MiB为MB也是可以的。
同样根据硬盘厂商与用户对于1MB大小的不同理解,所以好多160G的硬盘实际容量按计算机实际的1MB=1024KB算都不到160G,这也可以解释为什么新买的硬盘“缺斤短两”并没有它所标示的那么大。
注:10TB大约等于一个人脑的存储量。


字符人们使用的记号,抽象意义上的一个符号。 '1', '中', 'a', '$', '¥' ……
字节计算机中存储数据的单元,一个8位的二进制数,是一个很具体的存储空间。0x01, 0x45, 0xFA……
-----------------------------------------------------------------------------------------------------------------------------------------
单精度 与 双精度:
单精度和双精度数值类型最早出现在C语言中(比较通用的语言里面),在C语言中单精度类型称为浮点类型(Float),顾名思义是通过浮动小数点来实现数据的存储。这两个数据类型最早是为了科学计算而产生的,他能够给科学计算提供足够高的精度来存储对于精度要求比较高的数值。

通过解剖单精度数值的二进制存储格式,我们可以清楚看到,实际上单精度/双精度的存储,都要通过乘法和除法,其中必有舍入,如果恰好你的数值在除法中被舍入了,那么你赋的初值就很可能与你最终存储的值不完全相同,其中的微小差异,并不与单精度/双精度的设计目标相违背。???
当我们在数据库中或者VBA代码中使用一个单精度/双精度数值的时候,也许你从界面上看不到区别,但是在实际的存储中,这个差别却真真切切地就在那里,当你对其进行相等比较的时候,系统只是简单地作二进制的比较,界面上无法体现的微小差异,在二进制比较面前却无处遁形,于是,你的等于比较返回了一个意料之外的False。
(1)在内存中占有的字节数不同

?单精度浮点数在机内占4个字节

?双精度浮点数在机内占8个字节

(2)有效数字位数不同

?单精度浮点数有效数字8位

?双精度浮点数有效数字16位

(3)所能表示数的范围不同

?单精度浮点的表示范围:-3.40E+38 ~ +3.40E+38

?双精度浮点的表示范围:-1.79E+308 ~ +1.79E+308

(4)在程序中处理速度不同

一般来说,CPU处理单精度浮点数的速度比处理双精度浮点数快


单精度实数在内存中占32bit  有效数字为6~7位

双精度实数占内存单元为64bit  有效数字为15~16位

因为小数在计算机中存储的多数为近似值,虽然符点数的表示方法(IEEE754标准)可以使得四字节或八字节表示很大的数据,但是,由于有的小数不能完全转换成纯粹相等的二进制数,所以,计算机只能保存最接近的其值的二进制小数。
-----------------------------------------------------------------------------------------------------------------------------------------
  在Java语言中,提供了多种整型数据类型:byte、short、int、long。它们都是定义了一个整数,唯一的区别就是它们能够表示数据的范

围。

  能够表示数据的范围越大,占用的内存空间也就越大,因此,在程序设计中应该选择最合适的类型来定义整数。

  大家可以根据以下规律,记住它们所占的内存空间:

  1) int是最基本的,它占用32位(现代计算机不就是32位机嘛!); 4字节

  2) long,长的,也就是比int还长,它占用64位; 8字节

  3) short,短的,也就是比short还短,它占用16位; 2字节

  4) byte,字节,8位组成一个字节,当然byte就只占8位了。 1字节

  根据它们所占用的内存空间不同,能够表示的数字范围也不同。占用内存空间越大,能够表示的数字范围也就越广。
-----------------------------------------------------------------------------------------------------------------------------------------
定义一个常量时用final,并且常量名要大写。比如 final float PI=3.14;
-----------------------------------------------------------------------------------------------------------------------------------------
break (中断)跳出的是当前的整个for循环
continue(继续) 是不再执行continue 以后的代码 直接进行下次循环 它并没有跳出for整个循环
-------------------------------------------------------------------------------------------------------------------------------------
Java中的四种修饰属性 public     所有包 不同子包都可以访问到
             protected  同一包可以访问到,不同一包的非子类访问不到
             default    只有在同一包下可以访问到
             private    只有自己的类中可以访问到
封装性:封装就是隐藏实现细节, 将属性私有化, 提供公有方法访问私有属性。
1用private(私有的)说明权限
 当一个属性被封装时,可以定义一个方法让外部得到或者还回这个对象,    即:getXxx()与setXxx();
2 一个方法被封装起来时 看不到方法的过程,也不知道它是怎样运行的
-----------------------------------------------------------------------------------------------------------------------------------------
方法的重载与重写
方法的重载是在一个类中 方法名相同 参数不同;
方法的重写则是在继承中 父类与子类有相同的方法 ,但方法的内容不一样。具体的实现功能由子类中的方法去写。
-----------------------------------------------------------------------------------------------------------------------------------------
如果一个子类继承了父类,那么在子类调用构造方法实例化时,必须先调用父类中的构造方法(因为子类继承了父类,父类中所有公共的属性和方法都可以被子类调用,如果子类在实例化之后需要马上用到父类中的属性或者方法,而这时候父类还没有初始化,显然是要出问题的,所有必须先初始化父类空间。)
-----------------------------------------------------------------------------------------------------------------------------------------
StringBuffer是可变的字符串对象
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据。
自动装箱即基本数据类型被自动包装为包装类在JDK5之后才有这个功能。
-----------------------------------------------------------------------------------------------------------------------------------------
this与super
this  可以代表当前类的对象
this  只是在本类中使用               super 代表调用的是父类中的成员
this. 调用本类中的属性和普通方法     super.调用父类中的属性和普通方法
this() 调用本类中的构造方法          super()调用父类中的构造方法
-----------------------------------------------------------------------------------------------------------------------------------------
final 最终的
修饰的类不能被继承
修饰的变量变为常量
修饰的方法不能被重写
-----------------------------------------------------------------------------------------------------------------------------------------
toString 方法是在输出的时候会自动调用 如果你没有重写toString方法 调用的是超类Object类中的toString方法 返回的是对象的地址
-----------------------------------------------------------------------------------------------------------------------------------------
String 的俩种实例化的区别
String str="hello";
String str2="hello";
String str3="hello";
这种直接赋值的方式 实际上只在堆内存中创建了一个“hello” str str1 str2全指向一个 “hello”。字符串的内容一旦创建则不可更改。
String str4=new String(hello);会重新开辟新空间 存储hello
str +“Word”
第一种方式会在堆内存中 创建“Word” 的空间  最后再创建一个新空间 用来存储“ hello Word”str重新指向“helloWord”。
StringBuffer str5=new StringBurberry
str5 +“Word”;
它在堆内存中不会创建新的空间,会把原来的“hello”变为“hello Word”
-----------------------------------------------------------------------------------------------------------------------------------------
String 类是不可变类
StringBuffer类是可变类,任何对它所指代的字符串的改变都不会产生新的对象。  可变与不可变是说内容是否可变
StringBuffer和StringBuilder类的区别 :StringBuilder类不是线程安全的,但其在单线程中的性能比StringBuffer高。
采用String对象时,即使运行次数仅是采用其他对象的1/100,其执行时间仍然比其他对象高出25倍以上;而采用StringBuffer对象和采用StringBuilder对象的差别也比较明显,前者是后者的1.5倍左右。由此可见,如果我们的程序是在单线程下运行,或者是不必考虑到线程同步问题,我们应该优先使用StringBuilder类;当然,如果要保证线程安全,自然非StringBuffer莫属了。
StringBuffer和StringBuilder类长度都是可扩充的。
-----------------------------------------------------------------------------------------------------------------------------------------
String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
 简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
 而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:
 String S1 = “This is only a” + “ simple” + “ test”;
 StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);
 你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个
 String S1 = “This is only a” + “ simple” + “test”; 其实就是:
 String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
这时候 JVM 会规规矩矩的按照原来的方式去做

在大部分情况下 StringBuffer > String
StringBuffer
Java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append("le") 会使字符串缓冲区包含“startle”,而 z.insert(4, "le") 将更改字符串缓冲区,使之包含“starlet”。
在大部分情况下 StringBuilder > StringBuffer
java.lang.StringBuilde
java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同。
-----------------------------------------------------------------------------------------------------------------------------------------
多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
多态存在的三个必要条件
一、要有继承;
二、要有重写;
三、父类引用指向子类对象。
1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。如图8.3 所示。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。


Java中多态的实现方式:接口实现,继承父类进行方法重写,同一个类中进行方法重载。
-----------------------------------------------------------------------------------------------------------------------------------------
static 静态的
可以修饰成员变量 ,修饰方法,修饰代码块 。无论修饰什么 在加载的时候都会被优先加载。
静态修饰的方法或者成员变量不能调用非静态的成员变量或方法,因为静态变量或方法会优先加载,找不到非静态成员变量或方法
静态成员可以统计 类被加载了几次,因为每加载一次类都会先加载静态成员  (只要静态成员+=1 输出静态成员)
-----------------------------------------------------------------------------------------------------------------------------------------
主方法为什么不能被new 因为主方法是所有程序的入口 只有加载了主方法 才能继续加载其他代码;
主方法中的数组的作用:可以添加参数,在主方法被加载的同时把这些参数传递进去
具体方法:在有主方法的类中 点击右键 Run As 选择Run Configurations  有一排选项卡有7个 选择第二个点击 在第一个文本框中 输入第一个要传进去的参数 回车 输入第二个参数,以此类推   
如何显示:在主方法中输出:
        System.out.println(args[0]);
        System.out.println(args[1]);
即可。
-----------------------------------------------------------------------------------------------------------------------------------------
向上转型与向下转型     多态的使用        Animal animal=new Cat();
向上转型是:子类-----》父类 自动转型        animal=cat;            //子类对象cat赋给父类对象animal
向下转型是:父类-----》子类 需要强制转换     cat=(Cat)animal     //父类对象animal赋给子类对象需要强制转换

可以发生向下转型的另一个因素是父类的这个对象本来就new的是cat的对象如果你new的是Dog的对象那么上面的例子向下转型就会出问题。
为什么要强制转换?
因为多态父类对象只能使用子类中覆写父类的方法,不能使用子类中特有的方法,如果想使用,则必须将父类对象转换为子类对象(也就是向下转型)才能使用子类特有的方法。

有时候给你传过来一个父类中的对象,而你不知道它new的是哪个子类那么可以使用instanceof判断。
关键字instanceof
 if(animal instanceof Cat):
这个if可以判断animal这个父类对象new的是不是子类Cat
-----------------------------------------------------------------------------------------------------------------------------------------
abstract  修饰抽象
抽象类跟普通类一样 可以定义构造方法,成员方法,成员变量,但是抽象类不能通过构造方法实例化对象,它的构造方法是用来被子类调用的。
如果一个抽象类被继承,则必须重写这个抽象类中的抽象方法,(也就是由子类具体地实现这个方法)普通方法是可以不重写的。
注意:抽象类中可以存在变量和成员方法、构造方法。变量和成员方法将会被子类继承,可以通过多态的形式访问。
-----------------------------------------------------------------------------------------------------------------------------------------
接口interface(一个接口中只有抽象方法与被抽象修饰的变量(也就是常量))
接口与接口之间可以单继承, 类与类之间也是单继承,
类与接口之间是实现。
一个类支持单继承,多实现,(必须是先继承后实现,这个顺序不能变。)也就是一个类可以继承另一个类,同时可以实现多个接口。被继承的类如果是抽象类,那么这个继承的类必须实现被继承类的抽象方法(普通方法看你需不需要,需要就继承,不需要可以不继承,如果不是抽象类,则可以选择性继承不是强制性要求,如果是抽象的类中的抽象方法,则是强制性继承),()同时实现多个接口中的抽象方法。(如果这些接口中有继承了其他接口,则其他接口中的抽象方法也必须重写在你的类中重写(注意是在实现类中重写))
一个接口如果被继承,那么必须重写这个接口中的方法,就是抽象方法。   如果你不想重写,那么好,你也必须是抽象类。咱俩的抽象方法由继承你的那个类去实现。
(比如A是一个接口,你B继承了我,就必须对我负责(继承我的方法)是吧,如果你不想负责,那么可以找C类去继承你,然后C类就必须同时对咱俩负责,这样你就不用负责了。)
-----------------------------------------------------------------------------------------------------------------------------------------
JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地 址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。
-----------------------------------------------------------------------------------------------------------------------------------------
总结:抽象类和接口的区别
    共同点:
        1、都是用来实现抽象的,代表抽象层。
        2、都不能被实例化。  new
        3、都是包括抽象方法但不给出具体的实现。
    异同点:
        1、抽象类还是类,可以具备成员方法,而接口只能有抽象方法。
        2、一个类只能继承一个抽象类,但是可以实现多个接口。
        3、抽象类中添加一个普通方法,不会对子类造成影响,而对于接口,一旦公布就不应该改变。
-----------------------------------------------------------------------------------------------------------------------------------------
反射:主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
作用:1在运行时判断任意一个对象所属的类
      2在运行时判断任意一个类所具有的成员变量和方法
      3在运行时判断任意调用一个对象的方法
      4在运行时构造任意一个类的对象
什么是反射机制?
    简单说,反射机制值得是程序在运行时能够获取自身的信息,在Java中,只要有给定类的名字,那么就可以通过反射机制获取这个给定类的所有信息
Java反射机制提供了什么功能?
    在运行时能够判断任意一个对象所属的类
    在运行时构造任意一个类的对象
    在运行是判断任意一个类所具有的成员变量和方法    
    在运行时调用任意对象的方法
    在运行时创建新类对象
哪里用到反射机制?
    很多框架都用到反射机制,hibernate Struts 都是利用反射机制实现的 比较常见的有连接数据库时的代码Class.forname('com.MySql.jdbd.Driver.coass').newInstance();
反射机制的优缺点?
    静态编译:在编译时确定类型,绑定对象,通过
    动态编译:运行时确定类型,绑定对象。
    优点:反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类
    缺点:1性能问题:使用反射基本是一种解释操作。用于字段和方法接入时要远慢与直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用
          2:使用反射会模胡程序内部逻辑:程序员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比响应的直接代码更复杂。
-----------------------------------------------------------------------------------------------------------------------------------------
== 比较的是对象的地址 (地址值的比较)-128到127之间返回true
equals 没有重写之前比较的是对象的地址,重写(覆写)之后调用的是this这个类型(String或者Integer类)中的equals方法
equals 比较的是字符串中的内容(内容比较)
字符串中的内容一旦申明则不可改变。
-----------------------------------------------------------------------------------------------------------------------------------------
1、栈:由JVM分配区域,用于保存线程执行的动作和数据引用,存放的是函数的参数值,局部变量的值。栈是一个运行的单位,JAVA中一个线程就会相应地有一个线程栈与之对应。具体方法执行结束之后,系统自动释放JVM内存资源。

2、堆:由JVM分配的,存放由new创建的对象跟数组,JVM不定时地查看,如果没有引用指向这个对象就会被回收(垃圾回收机制)。

3、静态区(datasegment)—存放全局变量,静态变量和字符串常量,不释放

4、代码区(codesegment)—存放程序中方法的二进制代码,而且是多个对象共享一个代码空间区域

5、常量池:在编译阶段,在堆中分配出来的一块存储区域,用于存储显示的String,float 或者integer。例如:String str=“ABC”。“ABC”这个字符串是显示申明,所以存储在常量池。

      在方法(代码块)中定义一个变量时,java就在栈中为这个变量分配JVM内存空间,当超过变量的作用域后,java会自动释放掉为该变量所分配的JVM内存空间;
      在堆中分配的JVM内存由java虚拟机的自动垃圾回收器来管理,堆的优势是可以动态分配JVM内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配JVM内存的。
          缺点就是要在运行时动态分配JVM内存,存取速度较慢;
      栈的优势是存取速度比堆要快,缺点是存在栈中的数据大小与生存期必须是确定的无灵活性。
经过测试    String str="sram";
        String str1=new String ("sram");
        System.out.println(str==str1);
返回的是false 说明常量池里的“sram”与堆内存中的“sram”是不一样的。“==”比较的是对象地址的引用
        System.out.println(str.equals(str1));
返回的是true  说明equals 比较的是对象的内容。
-----------------------------------------------------------------------------------------------------------------------------------------
String和int直接赋值的区别。首先,最大的区别,一个是对象,一个是数值,对象可以为null,数值却必须有值,哪怕你没赋值,系统也会自动赋值为0。
-----------------------------------------------------------------------------------------------------------------------------------------
一个抽象类是不能用final 修饰的 因为抽象类就是用来被继承的 必须被子类覆写 不能继承的话就没有任何意义了。
-----------------------------------------------------------------------------------------------------------------------------------------
NULL代表声明了一个空对象,根本就不是一个字符串。只不过这个空对象没有指向堆内存中任何一个地址而已。   
“”代表声明了一个对象实例,这个对象实例的值是一个长度为0的空字符串。在堆内存中是有指向的 只不过长度为0而已。
-----------------------------------------------------------------------------------------------------------------------------------------
接口的特点:
接口是对外暴露的原则(因为接口就是用来被继承的,必须被子类覆写。)
接口是程序功能的扩展
接口的出现降低耦合性
接口可以多实现
类与接口之间是实现关系,接口与接口之间是继承关系。
一个类可以继承一个类的同时实现多个接口。
-----------------------------------------------------------------------------------------------------------------------------------------
抽象类与接口共性:都是不断抽取出来的抽象的概念。
-----------------------------------------------------------------------------------------------------------------------------------------
成员变量有默认初始化的值,局部变量没有默认的初始化值,必须赋值。
-----------------------------------------------------------------------------------------------------------------------------------------
字符串与int型之间的转换,要记得包装类
-----------------------------------------------------------------------------------------------------------------------------------------
集合中只用于存储对象,长度可变。数组还可以存储基本数据类型,长度是不可变的。
    List集合:List集合可以存在重复元素,怎么放怎么拿。
        ArrayList: 数组数据结构,查找方便。
            list中是否包含某个元素;
              方法:.contains(Object o); 返回true或者false
            如果是根据某种规则判断自定义类型,需要在自定义类中重写equals方法
        Vector: 与ArrayList一样,也是通过数组实现的。不同的是它支持线程的同步,  
              即一时刻只有一个线程能够写Vector,避免多线程同时写引起的不一致性。但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
        LinkedList: 链表数据结构实现的一个可变长容器,对头尾的操作灵活,插入快,查询慢。
    Set集合:Set集合中无法存储重复元素,存取顺序不一致
        HashSet: 底层是哈希表的数据结构。
            如何判断元素不重复?
            首先判断的是元素的哈希码,如果哈希码不一致,则元素不重复。如果哈希吗一致,则用equals方法比较俩个对象,返回true则是同一个对象,反之则不是同一个对象。
        TreeSet: 底层是二叉数数据结构。
            存放入TreeSet中的元素一定会自定义排序
            放入TreeSet的元素必须按照某种排序规则排序,如果没有排序规则,需要强制給元素添加排序
            第一种排序方法需要先让类继承compareable接口 然后重写compareTO方法
            第二种,自定义一个类专门用来排序
            第三种:匿名内部(只能使用一次)
     Map集合:将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。  
             HashMap  采用哈希表实现,所以无序  
             TreeMap   可以对健进行排序
-----------------------------------------------------------------------------------------------------------------------------------------
迭代器的next方法返回的值类型是object,所以要记得类型转换。
-----------------------------------------------------------------------------------------------------------------------------------------
泛型是没有办法指定基本数据类型的,必须设置成一个类

-----------------------------------------------------------------------------------------------------------------------------------------
枚举类不能继承其他类 因为枚举类已经继承了Java.lang.Enum类了

-----------------------------------------------------------------------------------------------------------------------------------------
invoke方法  参数:传一个对象,一个要调用方法的参数   返回 执行这个方法的结果  在Method类中

-----------------------------------------------------------------------------------------------------------------------------------------
多线程:Java多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。
    其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。
    1:继承Thread类的方法尽管是一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法。
        start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。
        MyThread myThread1 = new MyThread();  
        MyThread myThread2 = new MyThread();  
        myThread1.start();  
        myThread2.start();
    2、实现Runnable接口方式实现多线程
    如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口
    为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:
        MyThread myThread = new MyThread();  
        Thread thread = new Thread(myThread);  
        thread.start();
什么是线程?

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。Java在语言层面对多线程提供了卓越的支持,它也是一个很好的卖点。欲了解更多详细信息请点击这里。
2) 线程和进程有什么区别?

线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。更多详细信息请点击这里。
3) 如何在Java中实现线程?

在语言层面有两种方式。java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口所以你可以继承java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程。更多详细信息请点击这里.
4) 用Runnable还是Thread?

这个问题是上题的后续,大家都知道我们可以通过继承Thread类或者调用Runnable接口来实现线程,问题是,那个方法更好呢?什么情况下使用它?这个问题很容易回答,如果你知道Java不支持类的多重继承,但允许你调用多个接口。所以如果你要继承其他类,当然是调用Runnable接口好了。更多详细信息请点击这里。
6) Thread 类中的start() 和 run() 方法有什么区别?

这个问题经常被问到,但还是能从此区分出面试者对Java线程模型的理解程度。start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。更多讨论请点击这里
7) Java中Runnable和Callable有什么不同?

Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。它们的主要区别是Callable的 call() 方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。我的博客有更详细的说明。
8) Java中CyclicBarrier 和 CountDownLatch有什么不同?

CyclicBarrier 和 CountDownLatch 都可以用来让一组线程等待其它线程。与 CyclicBarrier 不同的是,CountdownLatch 不能重新使用。点此查看更多信息和示例代码。
9) Java内存模型是什么?

Java内存模型规定和指引Java程序在不同的内存架构、CPU和操作系统间有确定性地行为。它在多线程的情况下尤其重要。Java内存模型对一个线程所做的变动能被其它线程可见提供了保证,它们之间是先行发生关系。这个关系定义了一些规则让程序员在并发编程时思路更清晰。比如,先行发生关系确保了:

    线程内的代码能够按先后顺序执行,这被称为程序次序规则。
    对于同一个锁,一个解锁操作一定要发生在时间上后发生的另一个锁定操作之前,也叫做管程锁定规则。
    前一个对volatile的写操作在后一个volatile的读操作之前,也叫volatile变量规则。
    一个线程内的任何操作必需在这个线程的start()调用之后,也叫作线程启动规则。
    一个线程的所有操作都会在线程终止之前,线程终止规则。
    一个对象的终结操作必需在这个对象构造完成之后,也叫对象终结规则。
    可传递性

我强烈建议大家阅读《Java并发编程实践》第十六章来加深对Java内存模型的理解。
10) Java中的volatile 变量是什么?

volatile是一个特殊的修饰符,只有成员变量才能使用它。在Java并发程序缺少同步类的情况下,多线程对成员变量的操作对其它线程是透明的。volatile变量可以保证下一个读取操作会在前一个写操作之后发生,就是上一题的volatile变量规则。点击这里查看更多volatile的相关内容。
11) 什么是线程安全?Vector是一个线程安全类吗? (详见这里)

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。很显然你可以将集合类分成两组,线程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。
12) Java中什么是竞态条件? 举个例子说明。

竞态条件会导致程序在并发情况下出现一些bugs。多线程对一些资源的竞争的时候就会产生竞态条件,如果首先要执行的程序竞争失败排到后面执行了,那么整个程序就会出现一些不确定的bugs。这种bugs很难发现而且会重复出现,因为线程间的随机竞争。一个例子就是无序处理,详见答案。
13) Java中如何停止一个线程?

Java提供了很丰富的API但没有为停止线程提供API。JDK 1.0本来有一些像stop(), suspend() 和 resume()的控制方法但是由于潜在的死锁威胁因此在后续的JDK版本中他们被弃用了,之后Java API的设计者就没有提供一个兼容且线程安全的方法来停止一个线程。当run() 或者 call() 方法执行完的时候线程会自动结束,如果要手动结束一个线程,你可以用volatile 布尔变量来退出run()方法的循环或者是取消任务来中断线程。点击这里查看示例代码。
14) 一个线程运行时发生异常会怎样?

这是我在一次面试中遇到的一个很刁钻的Java面试题, 简单的说,如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的uncaughtException()方法进行处理。
15) 如何在两个线程间共享数据?

你可以通过共享对象来实现这个目的,或者是使用像阻塞队列这样并发的数据结构。这篇教程《Java线程间通信》(涉及到在两个线程间共享对象)用wait和notify方法实现了生产者消费者模型。
16) Java中notify 和 notifyAll有什么区别?

这又是一个刁钻的问题,因为多线程可以等待单监控锁,Java API 的设计人员提供了一些方法当等待条件改变的时候通知它们,但是这些方法没有完全实现。notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。而notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行。我的博客有更详细的资料和示例代码。
17) 为什么wait, notify 和 notifyAll这些方法不在thread类里面?

这是个设计相关的问题,它考察的是面试者对现有系统和一些普遍存在但看起来不合理的事物的看法。回答这些问题的时候,你要说明为什么把这些方法放在Object类里是有意义的,还有不把它放在Thread类里的原因。一个很明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。你也可以查看这篇文章了解更多。
18) 什么是ThreadLocal变量?

ThreadLocal是Java里一种特殊的变量。每个线程都有一个ThreadLocal就是每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了。它是为创建代价高昂的对象获取线程安全的好方法,比如你可以用ThreadLocal让SimpleDateFormat变成线程安全的,因为那个类创建代价高昂且每次调用都需要创建不同的实例所以不值得在局部范围使用它,如果为每个线程提供一个自己独有的变量拷贝,将大大提高效率。首先,通过复用减少了代价高昂的对象的创建个数。其次,你在没有使用高代价的同步或者不变性的情况下获得了线程安全。线程局部变量的另一个不错的例子是ThreadLocalRandom类,它在多线程环境中减少了创建代价高昂的Random对象的个数。查看答案了解更多。
19) 什么是FutureTask?

在Java并发程序中FutureTask表示一个可以取消的异步运算。它有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能取回,如果运算尚未完成get方法将会阻塞。一个FutureTask对象可以对调用了Callable和Runnable的对象进行包装,由于FutureTask也是调用了Runnable接口所以它可以提交给Executor来执行。
20) Java中interrupted 和 isInterruptedd方法的区别?

interrupted() 和 isInterrupted()的主要区别是前者会将中断状态清除而后者不会。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。而非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛出InterruptedException异常的方法都会将中断状态清零。无论如何,一个线程的中断状态有有可能被其它线程调用中断来改变。
21) 为什么wait和notify方法要在同步块中调用?

主要是因为Java API强制要求这样做,如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。还有一个原因是为了避免wait和notify之间产生竞态条件。
22) 为什么你应该在循环中检查等待条件?

处于等待状态的线程可能会收到错误警报和伪唤醒,如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下退出。因此,当一个等待线程醒来时,不能认为它原来的等待状态仍然是有效的,在notify()方法调用之后和等待线程醒来之前这段时间它可能会改变。这就是在循环中使用wait()方法效果更好的原因,你可以在Eclipse中创建模板调用wait和notify试一试。如果你想了解更多关于这个问题的内容,我推荐你阅读《Effective Java》这本书中的线程和同步章节。
23) Java中的同步集合与并发集合有什么区别?

同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。在Java1.5之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的扩展性。Java5介绍了并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和内部分区等现代技术提高了可扩展性。更多内容详见答案。
24) Java中堆和栈有什么不同?

为什么把这个问题归类在多线程和并发面试题里?因为栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。而堆是所有线程共享的一片公用内存区域。对象都在堆里创建,为了提升效率线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时volatile 变量就可以发挥作用了,它要求线程从主存中读取变量的值。
更多内容详见答案。
25) 什么是线程池? 为什么要使用它?

创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短的任务的程序的可扩展线程池)。更多内容详见这篇文章。
26) 如何写代码来解决生产者消费者问题?

在现实中你解决的许多线程问题都属于生产者消费者模型,就是一个线程生产任务供其它线程进行消费,你必须知道怎么进行线程间通信来解决这个问题。比较低级的办法是用wait和notify来解决这个问题,比较赞的办法是用Semaphore 或者 BlockingQueue来实现生产者消费者模型,这篇教程有实现它。
27) 如何避免死锁?


Java多线程中的死锁
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因为死锁会让你的程序挂起无法完成任务,死锁的发生必须满足以下四个条件:

    互斥条件:一个资源每次只能被一个进程使用。
    请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
    不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
    循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。这篇教程有代码示例和避免死锁的讨论细节。
28) Java中活锁和死锁有什么区别?

这是上题的扩展,活锁和死锁类似,不同之处在于处于活锁的线程或进程的状态是不断改变的,活锁可以认为是一种特殊的饥饿。一个现实的活锁例子是两个人在狭小的走廊碰到,两个人都试着避让对方好让彼此通过,但是因为避让的方向都一样导致最后谁都不能通过走廊。简单的说就是,活锁和死锁的主要区别是前者进程的状态可以改变但是却不能继续执行。
29) 怎么检测一个线程是否拥有锁?

我一直不知道我们竟然可以检测一个线程是否拥有锁,直到我参加了一次电话面试。在java.lang.Thread中有一个方法叫holdsLock(),它返回true如果当且仅当当前线程拥有某个具体对象的锁。你可以查看这篇文章了解更多。
30) 你如何在Java中获取线程堆栈?

对于不同的操作系统,有多种方法来获得Java进程的线程堆栈。当你获取线程堆栈时,JVM会把所有线程的状态存到日志文件或者输出到控制台。在Windows你可以使用Ctrl + Break组合键来获取线程堆栈,Linux下用kill -3命令。你也可以用jstack这个工具来获取,它对线程id进行操作,你可以用jps这个工具找到id。
31) JVM中哪个参数是用来控制线程的栈堆栈小的

这个问题很简单, -Xss参数用来控制线程的堆栈大小。你可以查看JVM配置列表来了解这个参数的更多信息。
32) Java中synchronized 和 ReentrantLock 有什么不同?

Java在过去很长一段时间只能通过synchronized关键字来实现互斥,它有一些缺点。比如你不能扩展锁之外的方法或者块边界,尝试获取锁时不能中途取消等。Java 5 通过Lock接口提供了更复杂的控制来解决这些问题。 ReentrantLock 类实现了 Lock,它拥有与 synchronized 相同的并发性和内存语义且它还具有可扩展性。你可以查看这篇文章了解更多
33) 有三个线程T1,T2,T3,怎么确保它们按顺序执行?

在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。你可以查看这篇文章了解更多。
34) Thread类中的yield方法有什么作用?

Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。它是一个静态方法而且只保证当前线程放弃CPU占用而不能保证使其它线程一定能占用CPU,执行yield()的线程有可能在进入到暂停状态后马上又被执行。点击这里查看更多yield方法的相关内容。
35) Java中ConcurrentHashMap的并发度是什么?

ConcurrentHashMap把实际map划分成若干部分来实现它的可扩展性和线程安全。这种划分是使用并发度获得的,它是ConcurrentHashMap类构造函数的一个可选参数,默认值为16,这样在多线程情况下就能避免争用。欲了解更多并发度和内部大小调整请阅读我的文章How ConcurrentHashMap works in Java。
36) Java中Semaphore是什么?

Java中的Semaphore是一种新的同步类,它是一个计数信号。从概念上讲,从概念上讲,信号量维护了一个许可集合。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore只对可用许可的号码进行计数,并采取相应的行动。信号量常常用于多线程的代码中,比如数据库连接池。更多详细信息请点击这里。
37)如果你提交任务时,线程池队列已满。会时发会生什么?

这个问题问得很狡猾,许多程序员会认为该任务会阻塞直到线程池队列有空位。事实上如果一个任务不能被调度执行那么ThreadPoolExecutor’s submit()方法将会抛出一个RejectedExecutionException异常。
38) Java线程池中submit() 和 execute()方法有什么区别?

两个方法都可以向线程池提交任务,execute()方法的返回类型是void,它定义在Executor接口中, 而submit()方法可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口,其它线程池类像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方法。更多详细信息请点击这里。
39) 什么是阻塞式方法?

阻塞式方法是指程序会一直等待该方法完成期间不做其他事情,ServerSocket的accept()方法就是一直等待客户端连接。这里的阻塞是指调用结果返回之前,当前线程会被挂起,直到得到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。更多详细信息请点击这里。
40) Swing是线程安全的吗? 为什么?

你可以很肯定的给出回答,Swing不是线程安全的,但是你应该解释这么回答的原因即便面试官没有问你为什么。当我们说swing不是线程安全的常常提到它的组件,这些组件不能在多线程中进行修改,所有对GUI组件的更新都要在AWT线程中完成,而Swing提供了同步和异步两种回调方法来进行更新。点击这里查看更多swing和线程安全的相关内容。
41) Java中invokeAndWait 和 invokeLater有什么区别?

这两个方法是Swing API 提供给Java开发者用来从当前线程而不是事件派发线程更新GUI组件用的。InvokeAndWait()同步更新GUI组件,比如一个进度条,一旦进度更新了,进度条也要做出相应改变。如果进度被多个线程跟踪,那么就调用invokeAndWait()方法请求事件派发线程对组件进行相应更新。而invokeLater()方法是异步调用更新组件的。更多详细信息请点击这里。
42) Swing API中那些方法是线程安全的?

这个问题又提到了swing和线程安全,虽然组件不是线程安全的但是有一些方法是可以被多线程安全调用的,比如repaint(), revalidate()。 JTextComponent的setText()方法和JTextArea的insert() 和 append() 方法也是线程安全的。
43) 如何在Java中创建Immutable对象?

这个问题看起来和多线程没什么关系, 但不变性有助于简化已经很复杂的并发程序。Immutable对象可以在没有同步的情况下共享,降低了对该对象进行并发访问时的同步化开销。可是Java没有@Immutable这个注解符,要创建不可变类,要实现下面几个步骤:通过构造方法初始化所有成员、对变量不要提供setter方法、将所有的成员声明为私有的,这样就不允许直接访问这些成员、在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝。我的文章how to make an object Immutable in Java有详细的教程,看完你可以充满自信。
44) Java中的ReadWriteLock是什么?

一般而言,读写锁是用来提升并发程序性能的锁分离技术的成果。Java中的ReadWriteLock是Java 5 中新增的一个接口,一个ReadWriteLock维护一对关联的锁,一个用于只读操作一个用于写。在没有写线程的情况下一个读锁可能会同时被多个读线程持有。写锁是独占的,你可以使用JDK中的ReentrantReadWriteLock来实现这个规则,它最多支持65535个写锁和65535个读锁。
45) 多线程中的忙循环是什么?

忙循环就是程序员用循环让一个线程等待,不像传统方法wait(), sleep() 或 yield() 它们都放弃了CPU控制,而忙循环不会放弃CPU,它就是在运行一个空循环。这么做的目的是为了保留CPU缓存,在多核系统中,一个等待线程醒来的时候可能会在另一个内核运行,这样会重建缓存。为了避免重建缓存和减少等待重建的时间就可以使用它了。你可以查看这篇文章获得更多信息。
46)volatile 变量和 atomic 变量有什么不同?

这是个有趣的问题。首先,volatile 变量和 atomic 变量看起来很像,但功能却不一样。Volatile变量可以确保先行关系,即写操作会发生在后续的读操作之前, 但它并不能保证原子性。例如用volatile修饰count变量那么 count++ 操作就不是原子性的。而AtomicInteger类提供的atomic方法可以让这种操作具有原子性如getAndIncrement()方法会原子性的进行增量操作把当前值加一,其它数据类型和引用变量也可以进行相似操作。
47) 如果同步块内的线程抛出异常会发生什么?

这个问题坑了很多Java程序员,若你能想到锁是否释放这条线索来回答还有点希望答对。无论你的同步块是正常还是异常退出的,里面的线程都会释放锁,所以对比锁接口我更喜欢同步块,因为它不用我花费精力去释放锁,该功能可以在finally block里释放锁实现。
48) 单例模式的双检锁是什么?

这个问题在Java面试中经常被问到,但是面试官对回答此问题的满意度仅为50%。一半的人写不出双检锁还有一半的人说不出它的隐患和Java1.5是如何对它修正的。它其实是一个用来创建线程安全的单例的老方法,当单例实例第一次被创建时它试图用单个锁进行性能优化,但是由于太过于复杂在JDK1.4中它是失败的,我个人也不喜欢它。无论如何,即便你也不喜欢它但是还是要了解一下,因为它经常被问到。你可以查看how double checked locking on Singleton works这篇文章获得更多信息。
49) 如何在Java中创建线程安全的Singleton?

这是上面那个问题的后续,如果你不喜欢双检锁而面试官问了创建Singleton类的替代方法,你可以利用JVM的类加载和静态变量初始化特征来创建Singleton实例,或者是利用枚举类型来创建Singleton,我很喜欢用这种方法。你可以查看这篇文章获得更多信息。
50) 写出3条你遵循的多线程最佳实践

这种问题我最喜欢了,我相信你在写并发代码来提升性能的时候也会遵循某些最佳实践。以下三条最佳实践我觉得大多数Java程序员都应该遵循:

    给你的线程起个有意义的名字。
    这样可以方便找bug或追踪。OrderProcessor, QuoteProcessor or TradeProcessor 这种名字比 Thread-1. Thread-2 and Thread-3 好多了,给线程起一个和它要完成的任务相关的名字,所有的主要框架甚至JDK都遵循这个最佳实践。
    避免锁定和缩小同步的范围
    锁花费的代价高昂且上下文切换更耗费时间空间,试试最低限度的使用同步和锁,缩小临界区。因此相对于同步方法我更喜欢同步块,它给我拥有对锁的绝对控制权。
    多用同步类少用wait 和 notify
    首先,CountDownLatch, Semaphore, CyclicBarrier 和 Exchanger 这些同步类简化了编码操作,而用wait和notify很难实现对复杂控制流的控制。其次,这些类是由最好的企业编写和维护在后续的JDK中它们还会不断优化和完善,使用这些更高等级的同步工具你的程序可以不费吹灰之力获得优化。
    多用并发集合少用同步集合
    这是另外一个容易遵循且受益巨大的最佳实践,并发集合比同步集合的可扩展性更好,所以在并发编程时使用并发集合效果更好。如果下一次你需要用到map,你应该首先想到用ConcurrentHashMap。我的文章Java并发集合有更详细的说明。

51) 如何强制启动一个线程?

这个问题就像是如何强制进行Java垃圾回收,目前还没有觉得方法,虽然你可以使用System.gc()来进行垃圾回收,但是不保证能成功。在Java里面没有办法强制启动一个线程,它是被线程调度器控制着且Java没有公布相关的API。
52) Java中的fork join框架是什么?

fork join框架是JDK7中出现的一款高效的工具,Java开发人员可以通过它充分利用现代服务器上的多处理器。它是专门为了那些可以递归划分成许多子模块设计的,目的是将所有可用的处理能力用来提升程序的性能。fork join框架一个巨大的优势是它使用了工作窃取算法,可以完成更多任务的工作线程可以从其它线程中窃取任务来执行。你可以查看这篇文章获得更多信息。
53) Java多线程中调用wait() 和 sleep()方法有什么不同?

Java程序中wait 和 sleep都会造成某种形式的暂停,它们可以满足不同的需要。wait()方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁,而sleep()方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。你可以查看这篇文章获得更多信息。
在java中wait和sleep方法的不同?

通常会在电话面试中经常被问到的Java线程面试问题。最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。

原创粉丝点击