Java程序员面试笔记(基础知识)

目录

1:Java语言

1.1:Java语言优点

1.2:Java与C++有什么异同

1.3:语法关键

1.4:为什么有的接口没有任何方法(成为标识接口)

1.5:Java clone

1.6:Java创建对象的四种方式

2:面向对象

2.1:面向对象有哪些特征

2.2:抽象类与接口

2.3:内部类

3:关键字

3.1:final,finally,finalize

3.2:assest

3.3:static

3.4:volatile

3.5:instanceof

4:基本数据类型与运算

4.1:基本数据类型

4.2:不可变类

4.3:i++和++i

4.4:无符号数右移

4.5:String,StringBuffer,StringBuilder,StringTokenizer(都是对字符串进行操作)

4.6:Java流

4.7:Java socket

4.8:Java序列化(序列化和外部序列化)

4.9:jvm加载class文件的机制

4.10:Java堆栈

5:集合

5.1:迭代器

5.2:Collection和Collections有什么区别

5.3:ArrayList,Vector,LinkedList区别

5.4:HashMap,HashTable,TreeMap,WeakHashMap区别

6:多线程

6.1:为什么使用多线程

6.2:如何实现Java多线程

6.3:run()和start()方法区别

6.4:sleep()和wait()方法异同

6.5:sleep()和yield()方法异同

6.6:守护线程(服务进程,精灵进程,后台线程)


1:Java语言

1.1:Java语言优点

  • Java为纯面向对象语言
  • 平台无关性(为解释型语言,由jdk解释为机器码)
  • Java提供了很多内置的类库
  • 提供了对web应用开发的支持
  • 具有较好的安全性和健壮性(安全机制:数组边界检测和Bytecode效验等)
  • 去除了c++中难以理解的易混淆的概念(指针,结构,多重继承等),使代码更严谨更简洁

1.2:Java与C++有什么异同

  • Java为解释型语言(源程序->被编译器编译成字节码->由jvm执行)(c++:编译型语言,源程序经过编译和链接之后生成可执行的二进制代码)。因此C/C++运行速度比Java快,但Java可跨平台运行;
  • Java为纯面向对象语言
  • 相比于c/c++,java没有指针概念,使程序更安全
  • Java不支持多继承,但是引入了接口。并且可以使用多态来达到多重继承的目的
  • 自动的垃圾回收机制,当垃圾回收器将要释放无用对象的内存时,会调用该对象的finalize方法;

1.3:语法关键

  • 其中main方法必须要有void static(保证在类还没加载时就可调用) public,除此之外可加final,synchnized
  • 在Java中,静态代码块不管顺序如何,都会在在类被加载时被调用,所以在类加载过程中执行顺序如下(父类静态代码块->子类静态代码块->父类非静态代码块->父类构造函数->子类非静态代码块(只有一个函数块)->子类构造函数)
  • 一个Java文件可以有多个类,但最多只能有一个被public修饰,且此类必须与文件名相同,但在编译时会生成多个字节码文件;

1.4:为什么有的接口没有任何方法(成为标识接口)

  • eg:Serializable。目的:为了唯一标识此类为何种信息。比如继承了Serializable接口的都需要序列化

1.5:Java clone

  • 前提:Java在处理基本数据类型时都是采用按值传递,其他类型都是按引用传递(其中赋值语句也是采用引用传递);
  • 使用clone步骤
  1. 实现clone的类首先需要继承Cloneable接口,Cloneable是一个标识接口,没有任何方法
  2. 在类中重写Object的clone方法
  3. 在clone方法中调用super.clone()方法,无论clone类的继承结构是什么,都会直接或者间接的调用Object类的clone方法;
  4. 把浅复制的引用指向原型对象新的克隆体

浅复制与深复制(判断是否有非基本数据类型)

  • 浅复制:调用clone方法后,其中的基本数据类型复制成功,引用类型复制失败(只复制了引用)
  • 深复制:调用clone方法后,基本类型数据复制成功,接着对对象的非基本数据也调用clone方法完成深复制

1.6:Java创建对象的四种方式

  • 通过new语句实例化一个对象
  • 通过反射机制创建一个对象(Class clazz = Class.forName("Sub");Base c = (Base)class.newInstance();),反射机制最重要的一个作用是可以在运行时动态的创建类的对象;
  • 通过clone方法创建一个对象
  • 通过反序列化的方式创建对象

2:面向对象

2.1:面向对象有哪些特征

  • 抽象:忽略一个主题中与当前目标无关的方面,以便更充分地注意与当前目标有关的方面,抽象分为过程抽象(抽象方法)和数据抽象(抽象类)
  • 继承:类的继承(注意继承和组合,尽量少使用继承降低程序耦合性),Java不支持多继承,子类只能继承父类的非私有(public 和 protected)变量和方法,子类成员变量或方法和父类是同名,子类会覆盖父类的成员变量和方法;通过getClass().getName()来获取父类的类名;
  • 封装:将客观事物抽象成类,每个类对自身的方法和数据实行保护
  • 多态:允许不同类的对象对同意消息做出响应,分为方法的重载(编译时多态,同一个类中有相同的方法,但参数不同)和方法的覆盖(运行时多态,子类可以覆盖父类的方法,运行时根据调用的类型判断那种方法体)。

2.2:抽象类与接口

如果一个类包含抽象方法,那么这个类就是抽象类,表示的是一个实体,接口表示的是一个概念

  • 抽象类:使用时不能被实例化,必须通过继承且实现抽象方法(否则也是抽象类)后实例化,声明抽象方法时不能写大括号;
  • 接口:接口中的成员变量默认都是public static final类型(所以在定义时就应该被初始化),接口中的方法只能用关键字public和abstract修饰,可以通过接口实现多继承;

2.3:内部类

静态内部类:static inner class

成员内部类:member inner class

局部内部类:local inner class

匿名内部类:anonymous inner class:参考Runnable;匿名内部类是一种没有类名的内部类,必须继承其他类或实现其他接口。其中匿名内部类不能有构造函数,不能定义静态成员和方法,不能是public protected private static,只能创建匿名内部类的一个实例,必须使用new来创建。

3:关键字

3.1:final,finally,finalize

  • final:声明属性,方法和类,分别表示属性不可变且必须被初始化,方法不可覆盖,类不可被继承
  • finally:作为try catch快的一部分,不管程序是否出现异常,finally的代码一定会执行;
try{
    return 0;
}catch(Exception e){
    return 1;
}finallly{
    return 2;
}
//finally一定会执行,为了防止这种情况,finally块中的代码是在return前执行;且finally中的return会覆盖别处的return,所以此代码返回2;
  • finalize:是Object类的一个方法,在垃圾回收器执行时会调用被回收对象的finalize方法,可以覆盖此方法来实现对其他资源的回收;

3.2:assest

  • assest(断言):作为一种软件调试的方法,主要作用是对一个boolean表达式进行检查
  • eg:assest 1+1 == 2 :"assest right";

3.3:static

  • static成员变量:可以通过static达到全局的效果;
  • static成员方法:不需要被创建对象就可调用(在单例模式中声明static方法);
  • static代码块:是独立于成员变量和成员函数的代码块的,不在任何一个方法体内,先于任何方法执行,且只会执行一次;
  • static内部类:可以不依赖于外部类实例对象而被实例,普通的内部类需要外类实例之后才可进行实例。

3.4:volatile

  • 保证了多线程的一致性
  • 被volatile修饰的变量,系统每次用到时都是直接从对应的内存当中去提取,而不是使用缓存中数据(详情参见jmm(Java线程内存模型))
  • 使用volatile会阻止编译器对代码的优化,因此会降低程序的执行效率;

3.5:instanceof

  • 判断一个引用类型的变量所指向的对象是否是一个类;eg:if(a instanceof String)

4:基本数据类型与运算

4.1:基本数据类型

  • byte short int long float double char boolean(都对应有封装类)

4.2:不可变类

  • 不可变类:当创建了这个类的实例后,就不允许修改他的值了;在Java类库中,所有基本类型的包装类都是不可变类,例如Integer,Float,其中String也是不可变类。
String s = "hello";
s+=" world";
//此时s变为了hello world
//String为不可变类,此操作是重新开辟了一块内存空间,重新引用了而已
String str = "hello world";
//此时创建str时,会在内存中找已经创建的,如果有相同的,那么直接创建引用,不需要重新开辟内存,即如下
if(str == s){
    System.out.print("true");
}
//此处返回true

//但new时不一样,例如
String str1 = new String("hello world");
//无论内存中是否已有该对象,都会重新开辟空间进行存储。
  • 如何创建一个不可变类
  1. 类中所有的成员变量都被private修饰
  2. 类中没有set方法(无法修改)
  3. 确保类中所有方法不会被子类覆盖(定义成final)
  4. 如果一个类成员不是不可变量,那么在初始化时或者使用get方法时,需要通过clone方法保证不可变性
  5. 有必要时,重写equal和hashcode方法
  6. tips:由于类的不可变性,在创建对象是就应初始化,因此最好提供一个带参构造方法进行初始化

4.3:i++和++i

  • i++:在程序执行完毕后进行自增
  • ++i:在程序执行开始前进行自增

4.4:无符号数右移

  • >>:有符号右移运算符(正数高位补0,负数高位补1)
  • >>>:无符号右移运算符(正负数高位都补0)
  • <<:左移运算符,左移n位表示原来的值乘2的n次方(常用来代替乘法),其中左移不区分无符号和有符号,都是在低位补0;

4.5:String,StringBuffer,StringBuilder,StringTokenizer(都是对字符串进行操作)

  • String:对字符串进行操作,但属于不可变类,可用赋值语句进行初始化
  • StringBuffer:属于可变类,其中必要时可对方法进行同步(保证线程安全),字符串如果经常需要修改时,尽量用StringBuffer。如果用String,会附加很多操作(先创建一个StringBuffer,在调用StringBuffer的append方法,最后调用toString方法进行返回),降低程序效率。其中StringBuffer只能通过构造函数进行初始化赋值。
  • StringBuilder:可修改字符串,非线程安全的,因此单线程时StringBuilder效率更高;
  • StringTokenizer:是用来分割字符串的工具类

4.6:Java流

  • 流的本质是数据传输
  • 字节流:继承于InputStream和OutputStream,在处理输入输出是不会用到缓存
  • 字符流:继承于Reader和Writer,使用缓存

4.7:Java socket

  • Soclet(套接字):分为面向连接的Socket通讯协议(tcp)和面向无连接的Socket通讯协议(udp),任何一个socket都是由Ip地址和端口号唯一确定;
//Socket服务端
ServerSocket server = new ServerScket(8080);
Socket socket = server.accpet();
br = new BufferReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream(),true);
String s = br.readLine();
//获取到输入的字符串
br.close();
pw.close();


//客户端
Socket socket = new Socket("127.0.0.1",8080);
br = new BufferReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream(),true);
pw.println("Hello");
String s = null;
while(true){
    s = br.readLine();
    if(s!=null){
        braek;
    }
}
br.close();
pw.close();

4.8:Java序列化(序列化和外部序列化)

4.8.1:序列化

  • 序列化可以将对象的状态写在流中进行网络传输,或者保存在文件数据库等系统中,并且在需要时把该对象读取出来重新构造一个相同的对象。实现Serializable接口即可,特点如下:
  1. 如果一个类可被序列化,那么它的子类也可被序列化
  2. 由于static,transient分别为静态和临时变量,所以此类型数据不会被序列化
  • 每个类都有特定的serialVersionUID,在反序列化的过程中,通过serialVersionUID来判断类的兼容性,最好在类的声明中西安市定义serialVersionUID(static final)

4.8.2:外部序列化,Externalizable

  • 使用Serializable时,类中的所有属性都会序列化,怎么实现部分序列化?
  1. 实现Externalizable接口,可以根据实际需求来实现readExtenrnal和writeExternal方法来控制序列化与反序列化所使用的属性。
  2. 使用transient来控制序列化的属性,被transient修饰的是临时属性,不会被序列化。

4.9:jvm加载class文件的机制

  • 参考之前博文:浅谈Java虚拟机及其优化
  • 类加载器将class文件加载到jvm中,具体是由ClassLoader和他的子类来实现的,类加载器本身也是一个类,本质是将类文件从硬盘读取到内存中。
  • 类加载分为显示加载和隐式加载
  1. 隐式加载:程序在使用new等方法创建对象时,会隐式的调用类的加载器把对应的类加载到jvm中
  2. 显示加载:通过直接调用class.forName()方法把所需的类加载到jvm中
  • 类加载器的主要步骤:
  1. 装载:根据查找路径找到相对应的class文件,然后导入
  2. 链接:分为三小步骤(检查,检查class文件的正确性。准备:给类中的静态变量分配存储空间。解析:将符号引用转换为直接引用)
  3. 初始化:对静态变量和静态代码块执行初始化工作

4.10:Java堆栈

  • 栈内存主要存放基本数据类型和引用类型。
  • 堆内存用来存放运行时创建的对象
  • JVM是基于堆栈的虚拟机,每个Java程序都运行在一个单独的JVM实例上,每一个实例唯一对应一个堆,一个Java程序内的多个线程也就运行到同一个JVM实例上,因此这些线程之间会共享堆内存。鉴于此,多线程在访问堆中数据时需要对数据进行同步;
  • 从功能来说,对主要用来存放对象的,栈主要用来执行程序的。较于堆,栈的存取速度更快,但栈的大小和生存期必须是确定的,因此缺乏一定的灵活性。堆却可以在运行时动态的分配内存,生存期不需要提前告诉编译器,这也导致了其存取速度缓慢。

5:集合

关于集合的详细知识参考上一篇博文:Java集合类汇总详解

5.1:迭代器

  • 迭代器是一个对象,为了遍历并选择序列中的对象,使用迭代器如下:
//Java关键代码
List<String> ll = new LinkedList<String>();
ll.add("first");
ll.add("second");
for(Iterator<String> iter = ll.iterator();iter.hasNext();){
    String str = (String)iter.next();
    System.out.println(str);
}
  1. 使用容器的iterator()方法返回一个Iterator,然后通过Iterator的next方法返回第一个元素;
  2. 使用Iterator的hasNext()判断容器中是否还有元素,如果有,可以使用next()获取下一个元素
  3. 可以通过remove()方法删除迭代器返回的元素

5.2:Collection和Collections有什么区别

  • Collection是一个集合接口,他提供了对集合对象进行基本操作的通用接口方法,实现接口的类主要有List和Set;
  • Collections是针对集合类的一个包装类,它提供一系列静态方法以实现对各种集合的搜索,排序,线程安全化等操作,其中大部分方法都是用来处理线性表;

5.3:ArrayList,Vector,LinkedList区别

  • 均为可变长数组
  • ArrayList:连续空间,可随机访问,超过初始化容量大小时进行扩容(ArrayList默认扩容1.5倍,没有提供方法来设置空闲扩充),非同步,非线程安全
  • Vector:连续空间,可随机访问,超过初始化容量大小时进行扩容(Vector默认扩容2倍,每次空间扩充的大小是可以设置的),大部分方法是直接或者间接synchronized同步的,线程安全
  • LinkedList:双向链表来实现,随机访问效率较低,插入数据效率较高,非线程安全。

5.4:HashMap,HashTable,TreeMap,WeakHashMap区别

  • map是用来存储键值对的数据结构
  • HashMap:根据键的HashCode值存储数据,是HashTable的轻量级实现(非线程安全的实现),允许一条key值为null,(较HashTable的contains方法,变成了containsvalue和containskey),hash数组默认大小是16,而且一定是2的倍数,HashMap中的key是强引用,当key没有被引用时,只有remove后,才能被垃圾回收(参考WeakHashMap)
//HashMap实现同步
Map m = Collections.synchornizedMap(new HashMap());
  • HashTable:不允许key为null,线程安全(效率低),hash数组默认大小是11,增加的方法是old*2+1,HashTable直接使用对象的hashCode
  • TreeMap:实现了SortMap接口,可以根据键排序;
  • WeakHashMap:WeakHashMap中的key不被外部引用之后,就会被垃圾回收器回收;

6:多线程

关于多线程的详细知识可以参考上一篇博文:详解Java线程创建和通讯(有后续)

6.1:为什么使用多线程

  • 使用多线程可以减少程序的响应时间;
  • 与进程相比,现成的创建和切换开销更小;
  • 多CPU或者多核计算机本身就有执行多线程的能力,如果使用单线程,将无法重复利用计算机资源,造成巨大的资源浪费
  • 使用多线程能简化程序的结构,使程序便于理解和维护

6.2:如何实现Java多线程

  • 继承Thread类,重写run方法
//示例
class MyThread extends Thread{
    public void run(){
        System.out.println("我run啦!");
    }
}

public class Test{
    public static void main(String[] args){
        MyThread my = new MyThread();
        my.start();
    }
}
  • 实现Runnable接口,并实现该接口的run方法
//示例
class MyThread implements Runnable{
    public void run(){
        System.out.println("我run啦!");
    }
}

public class Test{
    public static void main(String[] args){
        MyThread my = new MyThread();
        Thread t = new Thread(my);
        t.start();
    }
}
  1. 自定义类实现runnable接口,实现run方法
  2. 创建Thread对象,用实现Runnable接口的对象作为参数实例化该Thread对象
  3. 调用Thread的run方法
  • 实现Callable接口,重写call方法(与Runnable接口相比
public class CallableAndFuture{
    public static class CallableTest implements Callable<String>{
        public String call() throws Exception{
            return "我又run啦!";
        }
    }

    public static void main(String[] args){
        ExecutorService threadPool = Executor.newSingleThreadExecutor();
        Future<String> future = threadPool.submit(new CallableTest());
        try{
            System.out.println("waiting……");
            System.out.pringln(future.get());
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

//运行结果为
//waiting……
//我又run啦!
  1. Callable可以在任务结束后提供一个返回值,Runnab无法提供这个功能
  2. Callable中的call方法可以抛出异常,而Runna的run方法不能抛出异常
  3. 运行Callab可以拿到一个Future对象,来监视目标线程调用call方法的情况,当调用Future的get方法以获取结果时,当前线程就会阻塞,知道call方法结束返回结果

6.3:run()和start()方法区别

  • start():启动线程,此时线程处于就绪状态,在调度过程中,JVM通过调用线程类的run()方法来完成实际的操作,当run()结束时,线程就会终止;
  • run():直接调用时会当作一个普通的函数调用,程序中仍然只有main这一个主线程(其实还有垃圾回收线程)。start()方法可以异步的调用run()方法,直接调用时不能达到同步效果;

6.4:sleep()和wait()方法异同

  • sleep()是Thread类的静态方法,是线程用来控制自身流程的,会使线程短暂暂停执行。wait()是Object的方法,用于线程通讯,会使当前拥有该对象锁的进程等待知道notify()。
  • sleep()不会释放锁,wait()后,线程会释放掉他所占用的锁。
  • wait()必须放在同步控制方法或者同步语句块中使用,sleep()可在任何地方使用
  • sleep()必须捕获异常,wait()不需要捕获异常

6.5:sleep()和yield()方法异同

  • sleep()方法给其他线程运行机会时不考虑线程的优先级,yield()方法只会给相同优先级或更高优先级的线程已运行的机会。
  • sleep()方法执行后线程会进入阻塞状态,本线程短期内不会被执行,yield()方法只是使线程重新回到可执行状态,所以执行yield()方法的线程有可能会再次马上执行
  • sleep()抛出异常,yield()方法没有声明异常

6.6:守护线程(服务进程,精灵进程,后台线程)

  • 如果用户线程已经全部退出,只剩下守护线程了,那么jvm也就退出了
  • 守护线程一般有较低的优先级
  • 当一个守护线程中产生了其他线程,那么这新产生的线程默认还是守护线程。
  • 守护线程一个典型例子就是垃圾回收器
//设置守护线程
//在调用start()方法启动线程之前调用对象的setDaemon(true)方法
Thread t1 = new Thread();
t1.setDaemon(true);
t1.start();

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本书针对当前各大it企业面试笔试中常见的问题以及注意事项,进行了深层次的分析。本书除了对传统的计算机相关知识(c/c++、数据结构与算法、操作系统、计算机网络与通信、软件工程、数据库、智力题、英语面试等)进行介绍外,还根据当前计算机技术的发展潮流,对面试笔试中常见的海量数据处理进行了详细的分析。同时,为了更具说服力,本书特邀多位it名企面试官现身说法,对面试过程中求职者存在的问题进行了深度剖析,同时本书引入了一批来自于名牌高校、就职于明星企业的职场达人的真实求职案例,通过他们的求职经验与教训,抛砖引玉,将整个求职过程生动形象地展示在读者面前,进而对求职者起到一定的指引作用。本书也对各种类型的it企业的招聘环节进行了庖丁解牛式的分析,帮助求职者能够更加有针对性地 进行求职准备。 本书是一本计算机相关专业毕业生面试笔试的求职用书,同时也适合期望在计算机软硬件行业大显身手的计算机爱好者阅读。 程序员面试笔试宝典 目录 前言 上篇 面试笔试经验技巧篇 第1章 面试官箴言 2 第2章 面试心得交流 9 第3章 企业面试笔试攻略 20 第4章 面试笔试技巧 42 第5章 英文面试攻略 82 第6章 智力题攻略 102 下篇 面试笔试技术攻克篇 第7章 程序设计基础 122 第8章 数据库 240 第9章 网络与通信 254 第10章 操作系统 270 第11章 软件工程 278 第12章 发散思维 289 第13章 数据结构与算法 295 第14章 海量数据处理 390

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值