Java面试基础一
文章开始把我喜欢的这句话送个大家:这个世界上还有什么比自己写的代码运行在一亿人的电脑上更酷的事情吗,如果有那就是让这个数字再扩大十倍
1.C编译后生成机器可执行的exe文件即生成机器码
Java编译后由helloworld.java得到helloworld.class.class文件是字节码文件需要经过运行步骤经过JVM的作用生成机器可以执行的机器码,所以因为JVM的作用Java是跨平台的。
2.报错“无法从静态上下中引用非静态方法”——用类名引用了实例方法而非类方法
4.?优先级最高
5.读文件:
for(int i=0;i<MyFile.length();i++)
{
fileInputStream.read();
}
6.FileOutputStream fout = new FileOutputStream(file, true);//true 表示每次向file中写入数据的时候并不覆盖掉原来的数据而是在之前的文本后面继续添加新的内容
7. Swing和awt的区别
AWT是依靠本地方法来实现功能的,所以AWT控件称为“重量级控件”.而Swing ,不仅提供了AWT 的所有功能,还用纯粹的Java代码对AWT的功能进行了大幅度的扩充。由于Swing是用纯粹的Java代码来实现的,因此Swing控件在各平台通用。因为Swing不使用本地方法,故Swing控件称为“轻量级控件”。
AWT和Swing之间的区别:
1)AWT 是基于本地方法的C/C++程序,其运行速度比较快;Swing是基于AWT的Java程序,其运行速度比较慢。
2)AWT的控件在不同的平台可能表现不同,而Swing在所有平台表现一致。
Container类是java.awt.Component类的子类,JComponent类又继承自Container类。因此,JComponent类是AWT和Swing的联系之一。
除了Swing顶层容器类(top level containers)以外,其余所有的Swing组件类都继承自JComponent类
Swing顶层容器类包括了JFrame、JDialog、JApplet、JWindow,它们为其他的Swing组件提供了绘制自身的场所。
8.Java Applet 基础
如下所示是独立的Java 应用程序和applet 程序之间重要的不同:
- Java 中Applet 类必须继承自java.applet.Applet 类。(或javax.swing.Japplet类)
- Applet 类没有定义main(),所以一个Applet 程序不会调用main() 方法。
- Applet 被设计为嵌入在一个HTML 页面。
- 当用户浏览包含Applet 的HTML 页面,Applet 的代码就被下载到用户的机器上。
- 要查看一个Applet 需要JVM。JVM 可以是Web 浏览器的一个插件,或一个独立的运行时环境。
- 用户机器上的JVM 创建一个Applet 类的实例,并调用Applet 生命周期过程中的各种方法。
- Applet 有Web 浏览器强制执行的严格的安全规则,Applet 的安全机制被称为沙箱安全。
- Applet 需要的其他类可以用Java 归档(JAR)文件的形式下载下来。
9.MouseAdapter//MouseAdapter的类是一个抽象类(适配器)用于接收鼠标事件。这个类的所有方法都是空的。这个类是方便的类创建监听器对象。
MouseEvent类---作为传入参数中的那个 event e 。事件类共有一个函数getSource()返回事件源
MouseListener接口
MouseMotionListener接口
MouseListener是鼠标点击事件的监听者,也就是单击,双击,单击后弹起之类的动作就需要这个监听者,总之就是要点击鼠标的
MouseMotionListener是鼠标移动事件的监听者,也就是moveover,moveout之类的事件,也就是只移动鼠标不点鼠标的
MouseListener与MouseAdapter的区别
我们知道当一个类实现一个接口时,即使不准备使用某个方法,也必须给出接口中所有方法的实现,适配器可以替代接口来处理事件,当java提供处理事件的接口中多于一个方法时,java就相应的提供一个适配器类,比如:MouseAdapter,windowAdapter等等。
适配器已经实现了相应的接口,例如MouseAdapter类实现了MouseListener接口,因此可以使用MouseAdapter的子类创建的对象做监视器,只需重写需要的接口方法即可。
一个是类,一个是接口。
10.keyEvent类-----最重要方法getKeyCode()
getKeyChar():处理的是比较高层的事件,返回的是每欠敲击键盘后得到的字符(中文输入法下就是汉字)。
getKeyCode():键盘上每一个按钮都有对应码(Code),可用来查知用户按了什么键,返回当前按钮的数值
getKeyText():返回与此事件中的键关联的字符。比如getKeyText(e.getKeyCode())就返回你所按下的键盘
keyListener 接口
11.FocusEvent类经常使用getSource()
FocusListener接口
12.Frame Applet都是容器,Panel是面板
布局方式:FlowLayout,GridLayout BorderLayout(Dialog,Frame默认布局),CardLayout
每个容器都有setLayout方法
13.Component类是AWT中所有图形组件类的父类,除菜单外
MenuItem加到Menu上再加到MenuBar上
制作弹出菜单时一般使用PopMenu
14.操作图像使用image类
音频播放AudioClip
15.toString() 是object类型对象共有的方法
*.valueOf()可以实现类型转化
(string)、toString()和String.valueOf()三种类型转换
1、(String)
这是标准的类型转换,将object转成String类型的值。使用这种方法时,需特别小心的是因定义为Object 类型的对象在转成String时语法检查并不会报错,这将可能导致潜在的错误存在。这时要格外小心。
例:Object obj = new Integer(100); String strVal = (String)obj;
在运行时将会出错,因为将Integer类型强制转换为String类型,无法通过。
但是, 如果obj为null,不会报错,因null值可以强制转换为任何java类类型,(String)null也是合法的。
2、toString()
在java中,从严格意义上任意的java对象都可以调用toString(),但是有一点要注意,这个值不能为null,否则会报空指针异常。采用这种方法时,通常派生类会覆盖Object里的toString()方法
3、String.valueOf()
toString()是String.valueOf()的基础,但不同的是String.valueOf()自带了判断为null的条件,当为null时,String.valueOf()返回的是“null”。
在看java jdk String.valueOf()的源码时是这样的:
public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); }
从这个可以看出它用了表达式来判断,如果为null的时候,则自动返回一个为“null”的字符串。这样就无须担心该obj是否为空而报异常了。
1.基本类型传值,引用类型传引用
int a= 9
increment(a)//a仍然是9 如果a是对象,传对象则变了
//increment 函数实现a++
2.String类
StringBuffer类(jdk1.4同步适合多线程):append() insert()reverse()
StringBuilder(jdk5之后StringBuffer的非同步版本)
在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的
如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。
所以
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
易错点
方式一创建string:
String s1 = ‘java’
String s2 = ‘java’
s1==s2//true
方式二创建string:
String s1 = new String(‘java’)
String s2 = new String(‘java’)
s1==s2//false
方式一创建string如果常量池当中已有‘Java’则不创建s1,直接指向那里,如果没有则创建 所以==(比地址)一定相等
方法二创建时是在堆中创建,==比地址不相等
equals比值
3.数组创建时未分配空间,只有new之后才分配空间
int [] a //此时未分配空间
16.向量vector类类似于结构体可以存放不同类型的成员
17当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
- 然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
- 尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
- 第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
- A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。
- B.每个对象只有一个锁(lock)与之相关联。
- C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
- D. 一般的同步方法与synchronized (this)效果一样,都是锁定调用这个同步方法对象(该类的一个具体实例)
- E. 静态同步方法与synchronized (Class.this)效果一样,锁定的是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
- F. 如果同时定义一般的同步方法和静态同步方法,那么这个类的同一对象Obj。在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象
- public synchronized void methodAAA(){
- //….
- }
- 这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized关键字的方法。上边的示例代码等同于如下代码:
public void method3(SomeObject so){
synchronized(so){
//…..
}
}
这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:
class Foo implements Runnable{
private byte[] lock = new byte[0]; // 特殊的instance变量 实例变量
public void methodA() {
synchronized(lock) { //… }
}
//…..
}
注:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object() 则需要7行操作码。
1.多线程的实现有两种方法:继承Thread类或实现Runnable接口,该接口无start()等方法只有run()方法,已经继承了其他类就只能实现接口。
但Thread本质上也是实现了Runnable接口的一个实例
public class MyThread extends OtherClass implements Runnable
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
public class MaYi extends Thread
MaYi m1 = new MaYi("1号");
m1.start();
2.为了解决缓存不一致性问题,通常来说有以下2种解决方法:
1)通过在总线加LOCK#锁的方式
2)通过缓存一致性协议
这2种方式都是硬件层面上提供的方式。
在并发编程中,我们通常会遇到以下三个问题:原子性问题,可见性问题,有序性问题.
有序性问题:指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。
Java内存模型规定所有的变量都是存在主存当中(类似于前面说的物理内存),每个线程都有自己的工作内存(类似于前面的高速缓存)
只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。
对于可见性,Java提供了volatile关键字来保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。
先看一段代码,假如线程1先执行,线程2后执行:
1 2 3 4 5 6 7 8 |
|
这段代码是很典型的一段代码,很多人在中断线程时可能都会采用这种标记办法。但是事实上,这段代码会完全运行正确么?即一定会将线程中断么?不一定,也许在大多数时候,这个代码能够把线程中断,但是也有可能会导致无法中断线程(虽然这个可能性很小,但是只要一旦发生这种情况就会造成死循环了)。
每个线程在运行过程中都有自己的工作内存,那么线程1在运行的时候,会将stop变量的值拷贝一份放在自己的工作内存当中。
那么当线程2更改了stop变量的值之后,但是还没来得及写入主存当中,线程2转去做其他事情了,那么线程1由于不知道线程2对stop变量的更改,因此还会一直循环下去。但是用volatile修饰之后就变得不一样了:
第一:使用volatile关键字会强制将修改的值立即写入主存;
第二:使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效);
第三:由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。
那么线程1读取到的就是最新的正确的值。
自增操作不是原子性操作,而且volatile也无法保证对变量的任何操作都是原子性的。只能采用synchronized: