Java基础面试题

Introduction

目录

Introduction

 

一, Java语言特点

二, Java的基本数据类型有哪些, 各占几个字节

三, long(8)与float(4)的取值范围谁大谁小?

四, Java语言中的字符 char可以存储一个中文汉字吗? 为什么呢?

五, 看下面的程序是否有问题,如果有问题,请指出并说明理由。

六, 请自己实现两个整数变量的交换(不需要定义第三方变量)

七、最有效率的算出2 * 8的结果

八, 关于 switch的表达式下面正确的是

九, 面试题: return和 break以及 continue的区别?

十, 运算符 a++ 与 ++a 的区别

十一, Java 中到底是传值还是传地址?

十二, 指出下面变量的区别

十三, 面向对象特征

十四, 继承相关面试题一

十五, 继承相关面试题二

十六, 继承面试题三

十七, 方法重写重载的面试题

十八, 一个抽象类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义

十九, abstract不能与哪些关键字共存

扩展回顾; 关于final 与 static的修饰总结

二十, 面试题:package,import,class有没有顺序关系?

二十一, 成员内部类的面试题 掌握

二十二, 局部内部类访问局部变量的问题

二十三, 关于内部类的面试题; 按照要求,补齐代码

二十四, String 相关的特点

二十五, == 号与 equals 方法的区别

二十六, String类的面试题

二十七, 数组有没有 length方法,String中有没有 length属性

二十八, StringBuffer 和 StringBuilder 的区别

String 和 StringBuffer 的区别

二十九 看程序写结果(关于自动装箱...)

三十, 数组和集合的区别【面试题】

三十一, Vector 和 ArrayList的区别

三十二, ArrayList 和 LinkedList的区别

三十二, 你在开发中经常使用Java的哪些包?

三十三, 为什么自动生成 hashcode 的时有个31 的数

三十四, throws 和 throw 的区别

三十五, final, finally和 finalize的区别

三十六, 如果 catch里面有 return语句,请问finally的代码还会执行吗? 如果会,请问是在return前还是return后?

三十七, 如何使用异常处理 且 try-catch和 throws的区别:

三十八, 面试题: read() 方法读取的是一个字节,为什么返回是 int,而不是 byte?

三十九, 面试题: 找 bug;

四十, JVM 的启动是多线程的吗?【面试题】

四十一, Vector 与 ArrayList, StringBuffer 与 StringBuilder, Hashtable 与 HashMap.

四十二, 互斥锁(JDK1.5 新特性) 非面试题

四十三, 单例设计模式?

四十四, 饿汉式和懒汉式的区别?

四十五, 线程通讯相关问题

四十六, sleep方法和wait方法的区别?

四十七, 概述下简单工厂模式?

四十八, 类加载时机

四十九, 获取字节码对象(Class)的三种方式 (非面试题)

五十, 关于反射的使用要点总结 (非面试题)

五十一, 请简单谈谈你对动态代理的理解与使用

五十二, 模版(Template)设计模式概述和使用

五十三, 枚举概述 (非面试题, 可跳过不看)

五十四, JDK7, 8, 9 的新特性

五十五, GUI 是什么

事件处理三要素

五十六, 适配器设计模式(掌握)

五十七, 网络编程三要素

五十八, 正则表达式, 了解几个常用的构造即可, 如(X{n, m} -> \\d{4,14} -> 数字的个数为 4到14个之间)

100, 排序大集合

收集更新中....


 

一, Java语言特点

答: 简单性, 解释性, 面向对象, 高性能 分布式处理 多线程 健壮性 动态性 安全性 跨平台 移植性; 一般选择几个你熟悉的来回答就好. 如:

跨平台: Java语言开发的软件在Windows/ mac/ Linux 系统下都能运行, 只需要在操作系统上安装虚拟机 (Java Virtual Machine --- JVM )即可

移植性: 如果Java直接编译成系统能识的二进制码,可能一个标识在windows下是1100,而Linux 下是1001,这样java在windows下编译后无法在Linux 运行。所以java先编译成字节码(中间码),由JVM(java虚拟机来解释执行),而这个JVM对于主流的操作系统都有相应的版本,目的就是将统一的中间码编译成对应操作系统识的二进制码,然后执行。所以不论你在什么系统中编译的java,得到的都是统一的字节码(中间码)

面向对象性以及解释性语言

二, Java的基本数据类型有哪些, 各占几个字节

答: byte (1个字节) short (2个字节) int (4个字节) long(8个字节) float( 4 个字节) double(8个字节) char (2个字节) boolean(没有明确的说明大小)

三, long(8)与float(4)的取值范围谁大谁小?

虽然 long 是 8个字节, float 是 4个字节, 但是 float 的取值范围比 long 的取值范围大,因为 float 的算法与 long 的不一样:
0000 0000 0000 0000 ... 0000 0000 0000 0000
^         ^
s|<-- E-->|<--- M ....32个 bit
float = (-1)^S*M*2^E,float占用4个字节32个bit位,其中S占用第一个bit位,E占用第 2至第 9个bit位共 8个 bit位,M 占用第 10至第 32个 bit位。
那么 E 的存储空间为 2^8 = 256, 存储的数值范围定为 -2^7 ~ 2^7-1 (Ps: -128 ~ 127)
​
而 long 占 8个字节, 64个比特位, long 的存储容量为 2^64, 存储数值的范围定为 -2^63 ~ 2^63-1 , 所以 2^127 大于 2^63, float 的取值范围比 long 要大。

四, Java语言中的字符 char可以存储一个中文汉字吗? 为什么呢?

可以。因为 Java 语言默认是采用 Unicode 编码。一个 Unicode 编码是 16个 bit位, 所以一个用 Unicode 编码的中文汉字占 2个字节,  每个字符 char 类型的变量也是 2个字节。所以,Java中的 char 类型是可以存储一个中文汉字的. (ps: 但是 C语言中 char 类型只占 1个字节就不能存储汉字)
​
        char ch = '中';
        System.out.println("ch:"+ (int)ch);
        int max = Character.MAX_VALUE;
        int min = Character.MIN_VALUE;
        // 可简单验证下汉字在不在 字符编码的范围
        System.out.println("min ~ max: "+ min+ " ~ "+ max);
        System.out.println("min< ch <max: "+ (min <= ch && ch <= max));

打印结果如下:
    /*  ch:20013
        min ~ max: 0 ~ 65535
        min<char<max: true
     */

五, 看下面的程序是否有问题,如果有问题,请指出并说明理由。

short s=1; s = s+1; // 报错
short s=1; s += 1; // 编译通过
​
答: 类型不匹配问题, 等号右边short 类型数据 跟 默认的 int 类型的数据相加时, 会自动转化成 int 类型数据, 但是等号左边接收数据的类型, 却是 short 类型; 
而s += 1 不同由于是 += 是个操作符,在解析 s += 1 的时候, 就等价于 s = (short)(s+1) ; 左右两边都是 Short 类型, 应而编译通过;

六, 请自己实现两个整数变量的交换(不需要定义第三方变量)

位异或运算符的特点:
^的特点:一个数据对另一个数据位异或两次,该数本身不变。
​
int a = 1;
int b = 2;
a = a ^ b;
b = a ^ b;
a = a ^ b;

七、最有效率的算出2 * 8的结果

2 * 8 = 2*2^3 相当于 2 按位左移3位, 2 << 3;

八, 关于 switch的表达式下面正确的是

1.  byte 可以作为 switch 的表达式吗? 可以 
2.  long 可以作为 switch 的表达式吗?  可以
3.  String 可以作为 switch 的表达式吗?  也可以,但是必须jdk1.7以后才支持

九, 面试题: return和 break以及 continue的区别?

return 是结束当前方法 
break 是跳出当前循环
continue 是终止本次循环继续下次循环
break 和 continue 只能用在循环结构语句中        
return,不仅可以用在循环结构中,也可以用在循环结构外

十, 运算符 a++ 与 ++a 的区别

当++出现在变量后面,先将变量取出赋值,然后自身在加1

当++出现在变量前面,先自身加1,然后在给变量赋值

十一, Java 中到底是传值还是传地址?

Java传的只有值,因为地址值也是值。基本数据类型传的是值,引用数据类型数组和对象传的是地址,既是传地址,也是传值。

十二, 指出下面变量的区别

int[] x;
int[] y[];
int[] x,y[];  // x是一维数组,y是二维数组

十三, 面向对象特征

封装(encapsulation) 
继承(inheritance) 
多态(polymorphism)

十四, 继承相关面试题一

class Fu {
    public int num = 10;
    public Fu(){
        System.out.println("fu");
    }
}
​
class Zi extends Fu {
    public int num = 20;
    public Zi(){
        System.out.println("zi");
    }

    public void show(){
        int num = 30;
        System.out.println(num); // 30
        System.out.println(this.num); // 20
        System.out.println(super.num); // 10
    }
}
​
public class Test1 {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();
    }
}

十五, 继承相关面试题二

public class Test {
    public static void main(String[] args) {
        Zi z =new Zi();
    }
}
​
class Fu {
    static {
        System.out.println("静态代码块Fu"); // 1
    }
    {
        System.out.println("构造代码块Fu"); // 3
    }
    public Fu() {
        System.out.println("构造方法Fu");   // 4
    }
}
​
class Zi extends Fu {
    static {
        System.out.println("静态代码块Zi"); // 2
    }
​
    {
        System.out.println("构造代码块Zi"); // 5
    }
​
    public Zi() { // 内部系统默认有 super();
        System.out.println("构造方法Zi"); // 6
    }
}
​
ps: 静态代码块在类第一次加载进内存的时候就会执行, 且只执行一次 
    构造代码块在构造方法真正要执行之前就会执行, 每次调用构造方法都会执行一次

十六, 继承面试题三

class Person {
    static {
        System.out.println("Person 静态代码块"); // 3
    }

    {
        System.out.println("Person 构造代码块"); // 4 // 6
    }

    public Person() {
        System.out.println("Person 构造方法");   // 5 // 7
    }
}
public class Demo{
    static {
        System.out.println("Demo静态代码块"); // 1
    }
    public static void main(String[] args) {
        System.out.println("我是main方法");  // 2       
        Person p1 = new Person();
        Person p2 = new Person();
    }
}

十七, 方法重写重载的面试题

Overload 重载能改变返回值类型吗?
overload是指方法重载,重载可以改变返回值类型, 方法的重载只看参数列表的异同

Override 重写和 Overload 重载的区别?
Override 即方法重写: 也就是子类中出现了和父类中方法声明一模一样的方法。方法的重写是与返回值类型有关, 即返回值类型一致且具备继承关系
Overload 即方法重载:同一个类中方法名一样,但参数列表不同的方法。返回值类型随意, 不相关。

十八, 一个抽象类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义

可以,这么做的目的只有一个,就是不让其它类创建本类对象,交给子类完成

十九, abstract不能与哪些关键字共存

/*abstract和static
* 1.static修饰的方法是通过类名来调用
* 2.abstract修饰的方法必须由子类来实现,并由子类对象来调用方法
* 3.对象方法与类方法是冲突的
* 
 abstract和final
* 1.final修饰的方法是不能被重写的,所以也矛盾
* 
abstract和private
* 1.private修饰的方法不让子类访问,所以也矛盾
* */

扩展回顾; 关于final 与 static的修饰总结

final 关键字的修饰特点:

修饰类,类不能被继承,相当于做了丁克
修饰方法,那么这个方法不能被重写
修饰变量,变量就变成了常量,只能被赋值一次

 

 
* final 修饰变量叫做常量,一般会与 public , static 共用
* 常量命名规范,如果是一个单词,所有字母大写,
  如果是多个单词,每个单词都大写,中间用下划线隔开,
  如 public static final MAX_AGE  = 125;
* 如果修饰的变量是基本类型,是值不能被改变 
* 在定义变量时, 就马上初始化【这种初始化方法比较常用】
  在构造方法中, 进行初始化 [不常用]
  
* 修饰引用类型的变量,是地址值不能被改变,
  即对象本身不可变, 但对象中的属性可以改变
  修饰引用类型不可以赋值, 同类型的引用对象
  
final Person p = new Person("hxl", 18);
p.name = "xhl";
p.age = 16;
​
Person p2 = new Person();
p = p2; // 报错
p = new Person(); // 报错


static 关键字的特点:

随着类的加载而加载

优先于对象存在

被类的所有对象共享

如果某个成员变量是被所有对象共享的,那么它就应该定义为静态的。

 

 
1. static 声明的成员属性可以通过类名调用 
​
静态变量属于类,所以也称为为类变量
成员变量属于对象,所以也称为实例变量(对象变量)

 
推荐使用类名调用【强调】。
其实它本身也可以通过对象名调用。[不推荐, 会有警告]
静态修饰的内容一般我们称其为:与类相关的类成员
​
2. static 也可以用来修饰方法
静态方法只能访问静态的成员变量和静态的成员方法【掌握】
非静态方法可以访问静态的成员变量和静态的成员方法【掌握】
​
3. 在静态方法中是没有 this 关键字的, 如何理解呢?【掌握】
静态是随着类的加载而加载, this 是随着对象的创建而存在。 
静态比对象先存在。 
​
4. 如果一个类中所有的方法都是静态的
我们可以再多做一步, 私有构造方法, 不让其他类创建本类对象
确保用类名调用方法
​
public class Demo01 {
    public static void main(String[] args) {
//      Student stu = new Student();
        Student.test01();
        Student.say01();
        Student.speak01();
    }
}
​
class Student {
    String name;
    int age;

 
    private Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
​
    private Student() {
        super();
        // TODO Auto-generated constructor stub
    }
​
    static void test01() {
        System.out.println("test01");
    }

 
    static void say01() {
        System.out.println("say01");
    }

 
    public final static void speak01() {
        System.out.println("speak01");
    }
}

二十, 面试题:package,import,class有没有顺序关系?

1. package 只能有一个,必须放在java文件的第一行, 
​
2. import 只能放在 package 的下面,多个 import 应放在一起, 
​
3. 而 class 放在 package 或者import的下面

二十一, 成员内部类的面试题 掌握

//要求:使用已知的变量,在控制台输出30,20,10。
class Outer {
    public int num = 10;
    class Inner {
        public int num = 20;
        public void show() {
            int num = 30;
            System.out.println(); // num
            System.out.println(); // this.num
            System.out.println(); // Outer.this.num
        }
    }
}
​
class InnerClassTest {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer().new Inner();
        oi.show();
    }   
}

二十二, 局部内部类访问局部变量的问题

局部内部类, 访问它所在方法中的局部变量时, 必须用 final 修饰.

1. 局部内部类, 访问它所在方法中的局部变量必须用 final 修饰,为什么?

因为当调用这个方法时,局部变量如果没有用 final 修饰,他的生命周期和方法的生命周期是一样的,当方法弹栈,这个局部变量也会消失, 那么如果局部内部类对象还没有马上消失想用这个局部变量,就没有了, 如果用 final 修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也可以继续使用; 其实在 jdk 1.7之前的版本会报错, 不主动加 final 的话;
​public class Demo {

    public static void main(String[] args) {

        Outer outer = new Outer();

        outer.test();

    }

}

​

class Outer{

    public void test(){

        int a = 10;

        class Inner{

            public void test(){

                //如果是1.7的话,会报错; 1.8系统默认添加了 final 

                System.out.println(a);
            }

        }

​

        Inner inner = new Inner();

        inner.test();

    }

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

二十三, 关于内部类的面试题; 按照要求,补齐代码

 interface Inter { 

    void show(); 

}

​

class Outer { 

    //补齐代码 

    }

​
class OuterDemo {
    public static void main(String[] args) {
          Outer.method().show();
      }
}
​
// 要求在控制台输出 "HelloWorld"

 

----代码见下:

package lesson09;

​

interface Inter { 

    void show(); 
}
​
class A implements Inter{
    @Override
    public void show() {
        System.out.println("HelloWord");
    }
}

​

class Outer { 

    //补齐代码

    //第一种写法

    //public static A method(){

        //return new A();

    //}
​
    //public static Inter method(){ // 或者如此
        //return new A();
    //}
​
    //第二种写法:接口指向匿名内部类对象
    //方法的返回值类型是接口
    public static Inter method(){

        return new Inter() {

            @Override

            public void show() {

                System.out.println("HelloWord");
            }
        };
    }
}
​
class OuterDemo {

    public static void main(String[] args){

          Outer.method().show();

      }

}

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

二十四, String 相关的特点

string 类能不能被继承,或者说有没有子类

string 这个类是被 final修饰的,所以不能被继承,也就是说没有子类

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

二十五, == 号与 equals 方法的区别

== 是一个比较运算符号,即可以比较基本数据类型,也可以比较引用数据类据类型,
基本数据类型比较的是值,引用数据类型比较的是地址值,
equals 方法是一个方法,只能比较引用数据类型,所有对象都会继承object类中的方法,
如果对象所在的类, 没有重写 Object 类中的 equals 方法,equals方法和==号比较引用数据类型无区别,
重写后的 equals 方法比较的是对象中的属性, String 类本身已经重写 equals 方法, 所以比较的是字符串中的字符内容是否相等

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

二十六, String类的面试题

 1.判断定义为String类型的s1和s2是否相等

String s1 = "abc";

String s2 = "abc";

​

System.out.println(s1 == s2);//比较的是内存地址

System.out.println(s1.equals(s2));//比较的是内容是否相等

​

2.下面这句话在内存中创建了几个对象

String s1 = new String("ABC");// 常量池和堆区各一个, 出现一个副本
​
3.判断定义为 String 类型的s1和s2是否相等
String s1 = new String("abc");
String s2 = "abc";
​
一个堆区地址,一个常量区地址,肯定不一样

System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
​
4.判断定义为 String 类型的 s1 和 s2是否相等
//常量优化机制,编译的时候就已经是 abc
//int a = 3 + 7;编译的时候就已经是7
String s1 = "a" + "b" + "c";
String s2 = "abc";
System.out.println(s1 == s2); // true;
System.out.println(s1.equals(s2)); 
​
5.判断定义为 String 类型的 s1和s2是否相等
String s1 = "ab";
String s2 = "abc";
String s3 = s1 + "c"; // 此时的 s1 不是常量, 不具备常量优化机制
​

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

System.out.println(s3.equals(s2));

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

二十七, 数组有没有 length方法,String中有没有 length属性

int[] 数组没有 length 方法,只有 length 属性; 

String  是只有 length 方法,

​

int[] arr = {1,2,3,4,5};

System.out.println(arr.length);

String string = "abcdef";

int length = string.lenght();

 

 

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

二十八, StringBuffer 和 StringBuilder 的区别

String 是一个不可变的字符序列

​StringBuffer,  StringBuilder 是可变的字符序列
​
char[] chars = {'a','e','c','','','','','','','','','','','','',''}
​
String s = "abc"

 

 

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

二十九 看程序写结果(关于自动装箱...)

        Integer i1 = new Integer(97);
        Integer i2 = new Integer(97);
        System.out.println(i1 == i2);// false
        System.out.println(i1.equals(i2));// true
        System.out.println("-----------");
​
        Integer i3 = new Integer(197);
        Integer i4 = new Integer(197);
        System.out.println(i3 == i4);// false
        System.out.println(i3.equals(i4));// true
        System.out.println("-----------");
​
        Integer i5 = -127;// 自动装箱
        Integer i6 = -127;
        /**
         * 自动装箱,如果值一样,地址也一样 自动装箱,范围在-128 ~ 127 【256个数】的地址一样,其它范围地址不一样
         * 因为内部有个Integer的缓冲池
         */
        System.out.println(i5 == i6);// true ?
        System.out.println(i5.equals(i6));// true
        System.out.println("-----------");
​
        Integer i7 = -129;
        Integer i8 = -129;
        System.out.println(i7 == i8);// false
        System.out.println(i7.equals(i8));// true
         System.out.println("-----------");
​
        Double d1 = 1.1;
        Double d2 = 1.1;
        System.out.println(d1 == d2); // false <代表不存在缓存池>
        System.out.println(d1.equals(d2)); // true

 

 

三十, 数组和集合的区别【面试题】

区别1 : 数组, 既可以存储基本数据类型,又可以存储引用数据类型,基本数据类型存储的是值,引用数据类型存储的是地址值 集合, 只能存储引用数据类型(对象),集合中也可以存储基本数据类型,但是在存储的时候会自动装箱变成对象,int 一> Integer

区别2: 数组长度, 是固定的, 不能自动增长 集合长度, 是可变的, 可以根据元素的增加而增长

三十一, Vector 和 ArrayList的区别

Vector 是线程安全的,效率低, ArrayList 是线程不安全的,效率高. 共同点: 都是数组实现的

三十二, ArrayList 和 LinkedList的区别

ArrayList 底层是数组实现, 查询和修改快 LinkedList 底层是链表结构实现, 增和删比较快, 查询和修改比较慢; 共同点: 都是线程不安全的List有三个子类,
那我们到底使用谁呢? 查询多用 ArrayList 增删多用 LinkedList 如果都多 ArrayList

三十二, 你在开发中经常使用Java的哪些包?

java.util 包,这个包有很集合框架:List / Set / Map

三十三, 为什么自动生成 hashcode 的时有个31 的数

31是一个质数, 质数是能被1和自己本身整除的数,没有公约数

31这个数既不大也不小,大的话可能超过 int 的取值范围; 小的话,相同率机出现比较多

31 这个数好算, 2 的五次方减 1, 即 1向左移动 5位减 1即可 (1 << 5) - 1

三十四, throws 和 throw 的区别

throws
    用在方法声明的后面, 跟的是异常类名;
    可以跟多个异常类名, 用逗号隔开
    它表示抛出异常, 由该方法的调用者来处理

throw
    用在方法体内, 跟的是异常对象名
    只能抛出一个异常对象名, 表示抛出异常

三十五, final, finally和 finalize的区别

  
final 可以修饰类,不能被继承;修饰方法,不能被重写;修饰变量,只能赋值一次
​
finally 是 try 语句中的一个语句体,不能单独使用,用来释放资源
​
finalize 是一个方法,当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

 

 

 

 

 

 

 

 

三十六, 如果 catch里面有 return语句,请问finally的代码还会执行吗? 如果会,请问是在return前还是return后?

答:会执行,finally 的代码在 return 之前执行;
​
public class Demo01 {
​
    public static void main(String[] args) {
        int r = getDiv(10, 0);
        System.out.println("r=" + r);
    }
    
    //返回 a/b的结果
    public static int getDiv(int a,int b){
        int i = 0;
        try {
            i = a / b;
            return i;
        } catch (ArithmeticException e) {
            System.out.println("算术异常-除数不能为0");
            i = -1;
            return i; // 情况1, 返回 -1;
        }finally {
            System.out.println("final代码执行了...");
            /* 情况2, 依然返回 -1; 程序已经记住了前面 return 返回 -1;
               i = -2; */
            
            --------------无敌分割线, 情况3-----------------------
             // 情况3, 返回 -2; 因为最后来执行 finally 发现内部有 return ;
              i = -2;
              return i;
        }
        
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

三十七, 如何使用异常处理 且 try-catch和 throws的区别:

异常处理的使用原则: 如果自己能处理的问题,就用 try-catch, 如果自己不能解决的问题,就 throws

try-catch和 throws的区别:
    后续程序需要继续运行就用 try
    后续程序不需要继续运行就 throws
    如果JDK没有提供对应的异常,需要自定义异常。

三十八, 面试题: read() 方法读取的是一个字节,为什么返回是 int,而不是 byte?

- 字节输入流可以操作任意类型的文件, 比如图片音频等,
  这些文件底层都是以二进制形式的存储的
​
- 但是每次读取的数据都返回 byte 类型, 有可能在读取的过程中遇到 11111111, 
  而 11111111 在 byte 类型中为 -1, 程序是遇到 -1 就会结束读取, 
​
- 导致后面的数据就读不了了, 所以在读取的时候用 int 类型接收, 
  那么再遇到 11111111 的数据, 就会在其字节前面补上 3 个字节,
  此时 byte 类型的 -1 就变成 int 类型的 255了, 确保整个数据正常读取

 

 

 

 

 

 

 

 

 

 

 

 

 

三十九, 面试题: 找 bug;

// 定义一个文件输入流,调用read(byte[] b)方法, 将a.txt文件中的内容打印出来(byte数组大小限制为5)
        
        FileInputStream fis = new FileInputStream("a.txt");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        
        byte[] bytes = new byte[5];
        int len = 0;
        while((len = fis.read(bytes)) != -1) {
            baos.write(bytes, 0, len);
            /*
             * 把字节转成字符串, 会出现中文乱码问题
             * 因为byte 数组的长度是5 的话, 那么会取到汉字的一部分
             */
            System.out.println(new String(bytes, 0, len));
        }
        //内部会调用toString方法,把字节数组转成字符串
        System.out.println(baos);
        
        fis.close();

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

四十, JVM 的启动是多线程的吗?【面试题】

JVM 启动至少启动了垃圾回收的子线程和主线程,所以是多线程的。

main 方法的代码执行的位置就是在主线程(路径)
finalize() 这个方法在子线程(垃圾回收线程)执行

四十一, Vector 与 ArrayList, StringBuffer 与 StringBuilder, Hashtable 与 HashMap.

//1.面试题:Vector和ArrayList有什么区别 
    Vector 是线程安全的, ArrayList 是线程不安全的 
    Vector 的方法声明了 synchronized
    
//2.StringBuffer和StringBuilder有什么区别?
    StringBuffer 是线程安全的, StringBuilder 是线程不安全的 
    StringBuffer 的方法声明了 synchronized
    
//3.Hashtable和HashMap的区别
    Hashtable 是线程安全的, HashMap 是线程不安全的
    Hashtable 的方法声明了 synchronized

 

 

 

 

 

 

 

 

 

 

 

 

 

四十二, 互斥锁(JDK1.5 新特性) 非面试题

同步加锁
    使用ReentrantLock类[互斥锁]的lock()和unlock()方法进行同步 通信
    使用ReentrantLock类的newCondition()方法 可以获取Condition对象
    需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法

不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了 ( 详见三个线程间的通讯案例 )

四十三, 单例设计模式?

单例设计模式:保证类在内存中只有一个对象。
    对象是 new 出来的
    程序中,只能 new 一次对象

单例设计模式实现方式步骤:
    声明一个类,类中有一个静态属性,类型与类名相同	
    把空参构造方法声明为私有
    在类中提供一个公共静态访问方法来返回该对象实例

四十四, 饿汉式和懒汉式的区别?

饿汉式是空间换时间,懒汉式是时间换空间
在多线程访问时,饿汉式不会创建多个对象,而懒汉式有可能会创建多个对象
如果考虑线程安全问题,用饿汉式
如果不考虑线程安全问题,用懒汉式  
 
单例写法一: 饿汉式
单例写法二:懒汉式,用到时才New
单例写法三:简单
​
小段找感觉代码:
class Singleton {
    // 1, 饿汉式;
    private static Singleton instance = new Singleton();
    private Singleton() {}
    
    public static Singleton getInstance() {
        return instance;
    }
}
​
class Singleton { 
    // 2, 懒汉式; 没锁可能会有线程问题
    private static Singleton instance;
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
​
class Singleton {
    // 2, 最简式;
    public final static Singleton instance = new Singleton();
    private Singleton() {}
}
​

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

四十五, 线程通讯相关问题

 
1.什么时候需要通信?
* 多个线程并发执行时, 在默认情况下CPU是随机切换线程的, 如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印
​
2.怎么通信?
》如果希望线程等待, 就调用 wait()
》如果希望唤醒等待的线程, 就调用 notify();
》这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
》如果方法中没有同步锁,会有异常 IllegalMonitorStateException
​
3. 为什么wait方法和notify方法定义在Object这类中?
因为锁对象可以是任意对象, Object 是所有的类的基类,所以 wait 方法和 notify 方法需要定义在 Object 这个类中
​
4. 一些锁的概念
/** 
 * 1.锁问题:
 *   同步中,锁最好同一个对象,如果不是同一对象,相当还是会有线程安全
 *   锁:this,代码当前对象
 *   锁:如果 new 对象,就不是同一把锁
 *   锁:字节码对象 String.class,内存中,只有一个字节码对象
 *   开发中:一般都是this
 *   
 * 2.在方法内部声明synchronized的就是 “同步代码块”
 * 
 * 3.在声明方法的时候,添加 synchronized,就是同步方法
 *    》如果是非静态方法,锁就是this
 *    》如果是静态方法,锁就当前类的字节码对象
 *      //TicketTask.class
         public static synchronized void test1(){}
 *  
 * 4.同步使用的建议:
 *   同步加锁的时候,尽量让锁住的代码范围小一点,这样可以让其它线程等待时间少一点,性能高
 */

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

四十六, sleep方法和wait方法的区别?

 
》sleep 方法必须传入参数,参数就是时间,时间到了自动醒来
》wait 方法可以传入参数也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待
》sleep 方法在同步函数或同步代码块中,不释放锁,睡着了也抱着锁睡
》wait 方法在同步函数或者同步代码块中,释放锁
​
/**
 * sleep方法要传参数,抱着锁睡
 * wait方法可以传参数,也可以不用传,释放锁
 */

 

 

 

 

 

 

 

 

 

 

 

 

四十七, 概述下简单工厂模式?

又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例

优点: 客户端不需要在负责对象的创建,从而明确了各个类的职责

缺点: 这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护。

四十八, 类加载时机

1.创建类的实例 new Student()
2.访问类的静态变量,或者为静态变量赋值 Integer.MAX_VALUE
3.调用类的静态方法 Executors.newCachedThreadPool()
4.初始化某个类的子类 new Student() extend Person
5.直接使用 java.exe 命令来运行某个主类. java HelloWord
6.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

四十九, 获取字节码对象(Class)的三种方式 (非面试题)

1.Object 类的 getClass()方法,判断两个对象是否是同一个字节码文件 
2.静态属性 class, 锁对象 
3.Class类中静态方法forName()

五十, 关于反射的使用要点总结 (非面试题)

通过反射获取参构造方法
    1.如果要使用反射,先要获取字节码对象
    2.通过字节码对象的getConstructor()可以获取到构造方法对象
    3.构造方法对象(Contructor),有个newInstance方法创建这个字节码对象
    4.反射是在java.lang.reflect这个包中
    5.反射的作用一般是用于写框架(ssh,ssm)

通过反射获取类属性
    1.Class.getField(String)方法可以获取类中的指定字段(可见的),
    2.如果是私有的,可以用 getDeclaedField("name")方法获取
    3.通过set(obj, "李四")方法可以设置指定对象上该字段的值
    4.如果是私有的需要先调用 setAccessible(true)设置访问权限,
    5.调用 get(obj)可以获取指定对象中该字段的值

通过反射获取方法并使用
    1.反射中通过 Method类描述方法【构造方法:Contructor,字段:Field】
    2.通过 Class 的 getMethod 可以获取一个方法
    3.通过 getDeclaredMethod 可以获取私有方法
    4.如果要调用私有方法,设置访问权限 setAccessible

五十一, 请简单谈谈你对动态代理的理解与使用

 

 

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

五十二, 模

1.代理:本来应该自己做的事情,请了别人来做,被请的人就是代理对象。举例:春节回家买票让人代买	
2.在Java中 java.lang.reflect 包下提供了一个 Proxy类和一个 InvocationHandler接口
3.通过使用这个类和接口就可以生成动态代理对象。
4.JDK提供的代理只能针对接口做代理。
5.我们有更强大的代理 cglib
6.Proxy类中的方法创建动态代理类对象 Proxy 通过
newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)创建代理对象. InvocationHandler 的invoke(proxy,method, args) 方法会拦截方法的调用
 
一小段即兴代码 (非面试题)
/*
 * newProxyInstance方法用来返回一个代理对象,
 * 这个方法总共有3个参数,
 * 
 * ClassLoader loader用来指明生成代理对象使用哪个类装载器,
 * Class<?>[] interfaces用来指明生成哪个对象的代理对象,通过接口指定,
 * InvocationHandler h用来指明产生的这个代理对象要做什么事情。
 * 
 * 所以我们只需要调用newProxyInstance方法就可以
 * 得到某一个对象的代理对象了。
 */
UserService proxy = (UserService) Proxy.newProxyInstance(
        usi.getClass().getClassLoader(), 
        usi.getClass().getInterfaces(), 
        new InvocationHandler() {
​
            @Override
            public Object invoke(
                    Object proxy, 
                    Method method, 
                    Object[] args
                    ) throws Throwable {
​
                System.out.println(method);
                // 调用获取到的方法
                System.out.println("权限校验...");
                Object returnObj = method.invoke(usi, args);
                System.out.println("记录日志...");
                return returnObj;
            }
        });
​
// 用创建好的代理去调用方法
proxy.registerUser();
proxy.deleteUser();
proxy.updateUser();

版(Template)设计模式概述和使用

模版模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现
优点: 使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求
缺点: 如果算法骨架有修改的话,则需要修改抽象类

五十三, 枚举概述 (非面试题, 可跳过不看)

1.枚举是指将变量的值一一列出来,可以称为『数据集』举例:一周只有7天,一年只有12个月,一年有四个季节等。

2.Java中enum通过声明的类称为枚举类

3.枚举其实就是限定范围,防止不应该发生的事情发生

4.枚举注意事项
    · 定义枚举类要用关键字enum
    · 所有枚举类都是Enum的子类
    · 枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,
    · 但是如果枚举类有其他的东西,这个分号就不能省略。建议不要省略
    · 枚举类可以有构造器,但必须是private的,它默认的也是private的。
    · 枚举类也可以有抽象方法,但是枚举项必须重写该方法
    · 枚举在s5.tch语句中的使用
5.枚举是一个特殊类

五十四, JDK7, 8, 9 的新特性

 
* JDK7的六个新特性
    A:二进制字面量
    B:数字字面量可以出现下划线
    C:switch 语句可以用字符串
    D:泛型简化,菱形泛型
    E:异常的多个catch合并,每个异常用或|
    F:try-with-resources 语句 //这样写不用关流 (貌似多篇公号文章说这个是 JDK的新特性, 这...)
​
* JDK8
    接口中可以定义有方法体的方法
    如果是非静态,必须用 default 修饰   
    如果是静态的就不用了
​
* JDK9 
    1. 特殊标识符增加了限制 java8 之前可以 String _ = "hello"; 到了 9就不允许
    2. Java8之前 String的底层结构类型都是 char[], 到了 9替换成了 byte[];
    3. 引进 Httpclient, 以往我们通过 maven 添加 httpclient, java9中直接引入即可
    4. 模块化, 如果一个工程里有30个模块, 但只要运行其中一个模块, 便会带动所有模块; Java9之后, 运行某个模块, jvm 只会启动和它有依赖的模块;
​
* JDK10, 按计划 3月20日发布, 以下来自 LUPA 开源社区
    1. 局部变量的类型推断
    1. 将 JDK 的多个代码仓库合并到一个储存库中
    1. 垃圾收集器接口. 引入一个干净的垃圾收集器(GC)接口, 改善不同垃圾收集器的源码隔离性
    1. 线程局部管控. 允许停止单个线程, 而不是只能启用或停止所有线程
    1. 移除 Native-HeaderGenerationTool(javah)
    1. 额外的 Unicode 语言标签扩展. 包括 cu(货币类型) fw(每周第一天为星期几), rg(区域覆盖), tz(时区)等
​

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

五十五, GUI 是什么

Graphical User Interface(图形用户接口)

 

事件处理三要素
    事件处理的三要素:事件、事件源、监听器
        事件: 用户的一个操作,如:点击
        事件源: 被操作的组件,如:按钮
        监听器: 一个自定义类的对象, 实现了监听器接口, 包含事件处理方法,把监听器添加在事件源上, 当事件发生的时候虚拟机就会自动调用监听器中的事件处理方法

 

五十六, 适配器设计模式(掌握)

什么是适配器
    在使用监听器的时候, 需要定义一个类事件监听器接口.
    通常接口中有多个方法, 而程序中不一定所有的都用到, 但又必须重写, 这很繁琐.
    适配器简化了这些操作, 我们定义监听器时只要继承适配器, 然后重写需要的方法即可.

适配器原理
    适配器就是一个类, 实现了监听器接口, 所有抽象方法都重写了, 但是方法全是空的.
    适配器类需要定义成抽象的,因为创建该类对象,调用空方法是没有意义的
    目的就是为了简化程序员的操作, 定义监听器时继承适配器, 只重写需要的方法就可以了

 

五十七, 网络编程三要素

IP

* 每个设备在网络中的唯一标识
* 每台网络终端在网络中都有一个独立的地址,我们在网络中传输数据就是使用这个地址。
* IP地址的分类:IPv4 & IPv6
	* IPv4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已经用尽,IP格式: 192.168.1.168。
	* 测试网络连接 ping 192.168.1.68
	* 本地回路地址:ping 127.0.0.1(测试网卡有没插好)
	* 广播地址:255.255.255.255
	* IP地址也分公网地址(万维网使用)和私有地址(局域网使用),192.168.开头的就是私有址址
	* IPv6:8组,每组4个16进制数。1a2b:0000:aaaa:0000:0000:0000:aabb:1f2f(IPv6暂先不用掌握)

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

Port端口

* 端口号是每个程序在设备上的唯一标识
* 每个网络程序都需要绑定一个端口号,传输数据的时候除了确定发到哪台机器上,还要明确发到哪个程序,端口号范围从0-65535
* 编写网络应用就需要绑定一个端口号,尽量使用1024以上的,1024以下的基本都被系统程序占用了。
* 常用端口,面试会问
    * mysql: 3306
    * oracle: 1521
    * web: 80
    * tomcat: 8080

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

协议

* 协议是为计算机网络中进行数据交换而建立的规则、标准或约定的集合。网络传输方式协议有TCP&UDP
* TCP(传输控制协议)
	* 面向连接(三次握手),数据安全,速度略低。分为客户端和服务端。
	* 三次握手: 客户端先向服务端发起请求, 服务端响应请求, 传输数据
* UDP(数据报传输协议)
	* 面向无连接,数据不安全,速度快。不区分客户端与服务端。

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

五十八, 正则表达式, 了解几个常用的构造即可, 如(X{n, m} -> \\d{4,14} -> 数字的个数为 4到14个之间)

 
//正则表达式的构造摘要 - 字符类
//      [abc] a、b 或 c(简单类)
//      [^abc] 任何字符,除了 a、b 或 c(否定) 
//      [a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
//      [a-zA-Z_0-9] a 到 z 或 A 到 Z,_,0到9(范围)
//      [0-9] 0到9的字符都包括
//      [a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集) 
//      [a-z&&[def]] d、e 或 f(交集) 
//      [a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去) 
//      [a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去) 
​
//正则表达式的构造摘要 -预定义字符类
//      . 任何字符
//      \d 数字:[0-9] 
//      \D 非数字: [^0-9] 
//      \s 空白字符:[ \t\n\x0B\f\r] 
//      \S 非空白字符:[^\s] 
//      \w 单词字符:[a-zA-Z_0-9] 
//      \W 非单词字符:[^\w] 
​
//正则表达式的构造摘要 - 数量词
X? --> X,一次或一次也没有
X* --> X,零次或多次
X+ --> X,一次或多次
X{n} --> X,恰好 n 次 
X{n,} --> X,至少 n 次 
X{n,m} --> X,至少 n 次,但是不超过 m 次   

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

后记: 可能有笔误跟纰漏, 请及时指出, 以便迭代更新, 让我们一起来愉快的写 bug 吧 !


100, 排序大集合

 
public class Demo01 {
    public static void main(String[] args) {
​
    int[] arr = { 18, 30, 69, 29, 25, 10, 0 };
    // bubbleSort(arr);
    // selectionSort(arr);
​
    int value = 69;
    int index= BinaryRecursion.binaryRecursion(arr, value, 0, arr.length - 1);
    System.out.println("index: " + index);
}
​
    
​
    public static void selectionSort(int[] array) {
        // 2. 选择排序
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = i; j < array.length - 1; j++) {
                if (array[i] > array[j + 1]) {
                    array[i] = array[i] ^ array[j + 1];
                    array[j + 1] = array[i] ^ array[j + 1];
                    array[i] = array[i] ^ array[j + 1];
                }
            }
        }
​
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);
        }
    }
​
    public static void bubbleSort(int[] array) {
        // 1. 冒泡排序
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j] > array[j + 1]) {
                    array[j] = array[j] ^ array[j + 1];
                    array[j + 1] = array[j] ^ array[j + 1];
                    array[j] = array[j] ^ array[j + 1];
                }
            }
        }
​
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);
        }
    }
}
​
class BinaryRecursion {
​
    public static int binaryRecursion(int[] array, int value, int low, int high) {
        int mid = (low + high) / 2;
        if (low > high) { // 递归recursion 的基线
            return -1;
        }
        
        if (array[mid] == value) {
            return mid;
        }
        else if (array[mid] < value) {
            low = mid + 1;
            return binaryRecursion(array, value, low, high);
        }
        else { //  (array[mid] > value)
            high = mid - 1;
            return binaryRecursion(array, value, low, high);
        }
        
    }
}
​

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

收集更新中....

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值