黑马程序员——视频中的面试题

时间:2022-08-27 08:31:47

-------android培训java培训、期待与您交流!--------------

面试题

byte b = 2;

b = b + 3;  
这是错的,因为b在内存中占1个8位,3在内存中默认为int,占4个8位,
在执行b+3的时候,b自动提升了int类型与3运算,运算过后的结果还是int
类型,再将int类型的值赋给byte类型,这就会导致错误,除非进行强制类型
转换。如:b = (byte)(b+3);

byte b = 2;
b = (byte)b + 3;
这是错的,因为b原本就是byte,再将b转成byte没有意义,在进行b+3的时候,b依旧会提升为int类型

byte b = 2;
b += 3;
这是正确的,因为b+=3在进行运算时,它就做了强制转换,它有一个内置的强制类型转换

byte b = 3 + 3;
这是正确的,因为3+3在进行运算时,它就做了强制转换,在赋值的时候已经转成转换动作

byte b = 3;
b++;

这是正确的,因为b++在进行运算时,它就做了强制转换,在赋值的时候已经转成转换动作

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

5 % 1 = 0
5 % 2 = 1
1 % 5 = 1
2 % 5 = 2
3 % 6 = 3
左边如果小于右边,结果是左

1 % -5 = 1
5 % 2 = 1
-1 % 5 = -1
-5 % 2 = -1
如果运算时出现负数,看左边

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

char类型中能否装中文?

答:能,因为一个中文默认是两个字节,而char类型也是两个字节,所以可以装

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

以前喜欢考这种题,现在考不考不知道

int x = 3;
x += 4;

那么x的值是多少?

x += 4; 相当于  x = x + 4;,所以结果是 7。

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

无限循环的最简单表现形式

for( ; ; ){}

while(true){}

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

写出一个 冒泡排序 和 选择排序

选择排序:

for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}

冒泡排序:

for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {// -i:让每次比较元素少一个。-1避免数组越界
if (arr[i] > arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}

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

有一个 有序 的数组,想要将一个元素插入到该数组中,
还要保证该数组是有序的,如何获取该元素在数组中的位置?


答:可以通过折半的方式,去查找那个元素在数组中的位置,如果这个数存在,
那么就在存在的位置上把元素插入就行。如果元素不存在,则返回最小角标的值,就可以得到要插入的位置

代码演示:

public static int getIndex_2(int[] arr,int key){
int start = 0,end = arr.length-1;
int mid=(start+end)/2;
while(start<=end){
if(key>arr[mid])
start=mid+1;
else if(key<arr[mid])
end=mid-1;
else
return mid;//如果元素存在,则返回存在的位置
mid=(start+end)/2;
}
return -start;//如果元素不存在,则返回最小角标的值,该值就是插入的位置
}

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

 一维数组定义方式:int[] x;
  int x[];
 
二维数组定义方式:int[][] x;
  int x[][];
  int[] x[];
 
有两个数组为:int[] x,y[]; 那么一下的题中哪个是正确的?
 
1,x[0] = y; no
2,y[0] = x; yes
3,y[0][0] = x; no
4,x[0][0] = y; no
5,y[0][0] = x[0]; yes
6,x = y; no

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

什么是面向对象:


1.我通过电脑能够和对方视频,和对方聊天,那么电脑就是对象。因为我不需要直接去和对方面对面的说话
      而是通过电脑的形式直接和对方说话,我只知道电脑能够视频就行,而不用管它是怎么执行的,这就是面向对象。

2.汽车具备行驶的功能,而我要去某个地方,我可以使用汽车开过去。我不需要知道汽车的驱动是什么原理,也不
需要知道汽车为什么能够行驶。而我只需要知道汽车具备行驶的功能,我去调用汽车的行驶功能,汽车就能把我送到
目的地。那么,汽车就是一个对象,我只是在调用这个对象,而我不需要知道这个对象是如何实现它的功能。

3.手机的拨号功能,我输入拨号的号码后按下拨号键就能拨号,我不需要知道它是如何实现这个拨号功能的,
我只知道它能够拨号

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

class Person {
int age;
String name;
/*
构造代码块。
作用:给对象进行初始化。
对象一建立就运行,而且优先于构造函数执行。

和构造函数的区别:
构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化

构造代码块中定义的是不同对象共性的初始化内容
*/
{
System.out.println("构造代码块运行");
}
public Person(){
System.out.println("构造方法Person()运行");
}
public Person(String name){
System.out.println("构造方法Person(String name)运行");
}
}

public class Demo {
public static void main(String[] args) {
new Person();
new Person("zhangsan");

/*
结果:
构造代码块运行
构造方法Person()运行
构造代码块运行
构造方法Person(String name)运行
*/
}
}

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

class Demo1 {
int num = 9;

public Demo1() {
System.out.println("a");
}

static {// 静态代码块,给类初始化的
// num = 10; //此语句不能被执行,否则编译错误,因为静态代码块加载进类时num还没加载,所以静态代码块中无法存放num
System.out.println("b");
}
{// 构造代码块,给对象初始化的
System.out.println("c");
}

public Demo1(int x) {// 构造函数,给对应对象初始化的
System.out.println("d");
}

}

public class Demo {
public static void main(String[] args) {
new Demo1(5);// 结果:b c d
}
}

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

单例设计模式:

饿汉式: Single类一进内存,就已经创建好了Single对象

class Single{
private static final Single s = new Single();
private Single(){}
public static Single getSingle(){
return s
}
}

懒汉式:  Single类进内存,对象还没有存在,只有调用了getSingle方法时,才建立Single对象

class Single {
private static Single s = null;

private Single() {
}

public static Single getSingle() {
if (s == null) {
synchronized (Single.class) {
if (s == null) {
s = new Single();
}
}
}
return s;
}
}

开发时建议使用饿汉式

(面试专用题,必考题,不懂就背下来)
注意:懒汉式和饿汉式有什么不同?懒汉式的特点在于延迟加载,饿汉式不延迟加载。
      懒汉式的延迟加载有没有问题?有,如果多线程访问时会出现安全问题。
      怎么解决?可以加同步来解决,用同步代码块和同步方法都行,但是稍微有些低效。
用双重判断方式可以解决这个效率问题。
      加同步时候,使用的锁是哪一个?该类所属的字节码。

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

class Fu {
int num = 3;

void demo1() {
System.out.println("fu_1");
}

void demo2() {
System.out.println("fu_2");
}

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

class Zi extends Fu {
int num = 5;

void demo1() {
System.out.println("zi_1");
}

void demo3() {
System.out.println("zi_3");
}

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

public class Demo {
public static void main(String[] args) {
Fu f = new Zi();
f.demo1(); // 结果为zi_1 ,成员函数多态时,看右边

f.demo2(); // 结果为fu_2 ,成员函数多态时,看右边,右边有没此方法,则去父类中找

// f.demo3(); //编译失败,因为class Fu中没有demo3这个方法

f.demo4();//fu_4 ,静态方法多态时,看左边,左边是什么类型的类,就执行什么类型的类

System.out.println(f.num); // 结果为3 ,成员变量多态时,看左边。左边是什么类型的类,就执行什么类型的类

}
}

在多态中成员函数的特点:
在编译时期:参阅引用变量所属的类中是否有调用的方法(左边)。如果有,编译通过,如果没有编译失败。
在运行时期:参阅对象所属的类中是否有调用的方法(右边),如果有,则执行右边,没有,则执行左边

简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。

在多态中,成员变量的特点:
无论在运行还是编译时,都参考左边(引用型变量所属的类)。

在多态中,静态成员函数的特点:
无论运行还是编译时,都参考左边

在多态中,静态变量的特点:
无论运行还是编译时,都参考左边

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

外部其他类访问内部类的格式要记住,面试会考

访问格式:
1.当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中直接建立
  内部类对象。格式   外部类名.内部类名 变量名 = new 外部类名().new内部类名
2.当内部类在的成员位置上,就可以被成员修饰符所修饰。
  比如:private:  将内部类在外部类中进行封装,外部其他类无法访问该内部类
static:   内部类就具备了static的特性。   
当内部类被static修饰后,只能直接访问外部类中的static成员,出现了访问局限。


在外部其他类中,如何访问static内部类的非静态成员呢?
new Wai.Nei2().function_2();


在外部其他类中,如何访问static内部类非静态成员呢?
Wai.Nei2.function_3();

注意:当内部类中定义了static成员,那么内部类必须是static的


什么时候使用内部类:当事物中还有内部事物的时候,使用内部类(如:人是一个事物,而心脏是人的内部事物,那么
   可以将心脏建立为内部类)

class Wai{
int x = 3;
class Nei{
int x = 4;
static final int y = 3; //注意:非静态内部类中可以放 静态常量,但是不能放静态变量。如:static int y = 3;
public void function_1(){
int x = 5;
System.out.println("这是内部类的方法");
System.out.println(x); //结果为5
System.out.println(this.x); //结果为4
System.out.println(Wai.this.x); //结果为3
}
}

static class Nei2{
public void function_2(){
System.out.println("static内部类非静态方法");
}

public static void function_3(){
System.out.println("static内部类静态方法");
}
//public void function_4(){
// x = 7; //此语句错误,如果将外部类的x修饰为static那就正确,因为静态的内部类只能访问静态的成员变量
//}
}

// class Nei3{ //内部类中定义了static成员,那么内部类必须是static的
// public static void function_4(){
// System.out.println("非静态内部类的static方法");
// }
//}
}
public class Demo {
public static void main(String[] args) {
Wai.Nei n = new Wai().new Nei();//外部其他类中访问内部类的方式
n.function_1();
new Wai.Nei2().function_2();//外部其他类中访问static内部类的非静态成员方式
Wai.Nei2.function_3();//外部其他类中访问static内部类的静态成员方式
}
}

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

/*
面试题:
问:在没有父类或者接口情况下,能否使用匿名内部类吗?
答:能,代码如下
*/
public class Demo {
public static void main(String[] args) {
new Object(){
public void function(){
System.out.println("123");
}
}.function();


/*这个使用方式是错误的,Object o = new Object(){},
相当于Object o = new Object的子类,因为Obcect类中
没有function方法,所以不能调用function方法
Object o = new Object(){
public void function(){
System.out.println("123");
}
};
o.function();
*/
}
}

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

/*
问:这段代码编译会通过吗?

会,因为RuntimeException是一个特殊的异常,抛出异常时,可以不用进行声明


*/

class ExceptionDemo {
int div(int a, int b)//此处没有声明异常,编译会通过吗?
{
if (b < 0)
throw new RuntimeException();
return a / b;
}
}

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

/*
问:此程序中finally语句有执行到吗

没有执行到,因为发成异常时,在catch语句中执行了System.exit(0);导致了系统退出,所以finally没有执行到

*/

class Test {
public double function(double a, double b) throws Exception {
if (b <= 0)
throw new Exception();
return a / b;
}
}

public class Demo {
public static void main(String[] args) {
Test t = new Test();
try {
double num = t.function(3, 0);
} catch (Exception e) {
System.out.println(e.toString());
System.exit(0);//系统退出。当执行这里时,funally是执行不到的
} finally {
System.out.println("finally run");
}
System.out.println("over");
}
}

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

/*
* 写出程序结果
*/
class Wai{
int y = 6;
class Nei{
static int y = 3;
void show(){
System.out.println(y);
}
}
}

public class Demo {
public static void main(String[] args) {
Wai.Nei wn = new Wai().new Nei();
wn.show();
}
}
/*
* 编译失败:非静态内部类中不可以定义静态成员,静态常量除外
* 内部类中如果定义了静态成员,该内部类必须被静态修饰
*/

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

线程中,主函数直接调用run方法和调用start方法有什么区别?

答:主函数调用run方法和函数调用差不多,只有主线程在执行代码。
    当调用start方法时,则代表开启了一个线程,那么,程序中相当于有两个线程在执行。一个是主线程,一个是run方法

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

实现方式和继承方式有什么区别呢?

答:
 实现方式的好处:避免了单继承的局限性。
 定义线程时,建议使用实现的方式。

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

同步函数和静态同步函数用的锁是什么?

答:同步函数用的锁是this
静态同步函数用的锁是类的字节码  既:类名.class

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

/*
写一个死锁程序
*/
class Test1 implements Runnable {
public void run() {
while (true) {
synchronized (Test1.class) {
System.out.println("Test1...Test1.class");
synchronized (Test2.class) {
System.out.println("Test1...Test2.class");
}
}
}
}
}

class Test2 implements Runnable {
public void run() {
while (true) {
synchronized (Test2.class) {
System.out.println("Test2...Test2.class");
synchronized (Test1.class) {
System.out.println("Test2...Test1.class");
}
}
}
}
}

public class Demo {
public static void main(String[] args) {
new Thread(new Test1()).start();
new Thread(new Test2()).start();
}
}

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

String s1 = "abc";
String s2 = new String("abc");

问:s1和s2有什么区别呢?(面试题)

答:
s1在内存中有一个对象。
s2在内存中有两个对象。

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

面试题

/*
* JDK1.5 版本以后出现的新特性。
*/
public class Demo {
public static void main(String[] args) {
Integer a = new Integer(127);
Integer b = new Integer(127);
print(a==b);//false //不管是不是在-128~127范围 a和b都是不同对象
print(a.equals(b));//true

Integer c = new Integer(128);
Integer d = new Integer(128);
print(c==d);//false //不管是不是在-128~127范围 c和d都是不同对象
print(c.equals(d));//true

Integer x = 127;
Integer y = 127;//JDK1.5 以后,自动装箱,如果装箱是byte范围内(-128~127),如果该数值已经存在,则不会开辟新的空间
print(x == y);//true 注意:x和y指向了同一个对象,因为x和y在-128~127之间
print(x.equals(y));//false
Integer z = 128;
Integer k = 128;//因为128超过了byte范围,所以新开辟了一个空间
print(z == k);//false 注意:z和k是不同对象,因为z和k大于 byte范围(-128~127)
print(z.equals(k));//false

Integer i1 = 4;
Integer i2 = i1 + 2;// i1进行自动拆箱,变成int类型,和2进行加法运算。再将和进行自动装箱赋值给i2。 i1自动拆箱的方式是 i1.intValue();
}
public static void print(Object o){
System.out.println(o);
}
}

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

|--Collection

|--List:有序(存入和取出的顺序一致),元素都有索引(角标),元素可以重复

|--ArrayList:底层是数组数据结构。特点:查询速度很快,增删稍慢。线程不同步

保证唯一性原理:判断元素是否相同或者保证唯一性 只 依赖于equasl方法。ArrayList只会调用equals方法。

|--LinkedList:底层是使用链表数据结构。特点:增删速度很快,查询稍慢。线程不同步

|--Vector:底层是数组数据结构。现在基本不用,因为已经被ArrayList代替。增删,查询都很慢。线程同步。

|--Set:元素不能重复,无序。

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

保证唯一性原理:判断元素的hashCode值是否相同。
如果相同,还会继续判断元素的equals方法是否为true。

|--TreeSet:底层数据结构是二叉树,可以对Set集合中的元素进行排序。线程不同步

TreeSet第一种排序方式:

让元素自身具备比较性。元素要实现Comparable接口,覆盖ComparTo方法,
如果本类对象大于比较类,则返回正数。小于返回负数。等于返回0。
这种方式也称为元素的自然顺序,或者叫做默认顺序。

TreeSet第二种排序方式:

当元素自身不具备比较性时,或者具备的比较性不是所需要的。
这时就需要让集合具备比较性。
在集合初始化时,就有了比较方式

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

/*
面试会考addLast、addFirst的存入结果,
和removeLast、removeFirst删除的结果
*/
import java.util.*;

public class Demo {

public static void main(String[] args) {
// function1();
function2();
}

public static void function1() {
LinkedList link = new LinkedList();
link.addLast("java01");
link.addLast("java02");
link.addLast("java03");
print(link);// 结果:[java01, java02, java03]

print(link.getFirst());// 结果:java01
print(link.getFirst());// 结果:java01注意:这里是java01

print(link.removeFirst());// 结果:java01
print(link.removeFirst());// 结果:java02。注意:这里是java02,因为java01已经被删除,于是java02为第一个
}

public static void function2() {
LinkedList link = new LinkedList();
link.addFirst("java01");
link.addFirst("java02");
link.addFirst("java03");
print(link);// 结果:[java03, java02, java01]

// 不用迭代器遍历link
while (!link.isEmpty()) {//如果link不为空
print(link.removeFirst());// 结果:java03 java02 java01
}
}

public static void print(Object obj) {
System.out.println(obj);
}
}

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

问:在集合中,哪些集合是同步的,哪些集合是非同步的
答:在集合中除了Vector是同步的,其他的集合都是非同步的。在对非同步的
   集合进行多线程操作时,可以使用加锁的形式限定
-------------------------------------------------

问:ArrayList 和 HashSet判断元素是否相同,或者保证唯一性,依赖于什么方法?两者有什么不同?


答:ArrayList判断元素是否相同或者保证唯一性 只 依赖于equasl方法。ArrayList只会调用equals方法。
    HashSet判断元素是否相同或者保证唯一性不仅依赖于hasCode方法,还依赖于equals方法。
HashSet先调用hashCode方法,如果返回的hashCode值相同,则去调用equals方法。如果返回的hashCode值不同,则不调用equals方法

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

问:Hashtable 和 HashMap 有什么区别?
答:Hashtable  不可以 存入null键和null值。该集合是线程 同步 的。JDK1.0 出现的,效率低。
   HashMap  允许 使用null键和null值。该集合是 不同步 的。JDK1.2 出现的,效率高
   Hashtable 和 HashMap的相同点,都是哈希表数据结构

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

问:Collection和Collections有什么区别?


答:
Collection是集合类跟类,他的子类主要有Set 和List.
Collections是集合类的一个帮助类,他提供一些静态方法实现对各种集合的搜索、排序、线程安全化等操作。

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

double d1 = Math.ceil(16.34);//ceil返回大于指定数据的最小整数
double d2 = Math.floor(16.34);//floor返回小于指定数据的最大整数
print("d1="+d1);//17.0
print("d2="+d2);//12.0

double d3 = Math.pow(2, 3);//2的3次幂
print("d3="+d3);//d3=8.0

long long1 = Math.round(12.51);//四舍五入
long long2 = Math.round(12.49);//四舍五入
print("long1="+long1);//long1=13
print("long2="+long2);//long2=12

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


UDP:
1,面向无连接
2,数据封包,数据大小限制带64k内
3,是不可靠协议
4,传输速度快


哪些属于udp传输:聊天


TCP
1,面向连接
2,大数据量传输
3,是可靠协议
4,因为需要连接,所以效率低


哪些属于tpc传输:下载

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


列出5个常见异常:


IOException ArrayIndexOutOfBoundsExceptionInterruptedException
NullPointerException ConnectExceptoinBindException

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

/*
*1,用代码详细描述线程间的通信(等待唤醒机制)
*/
class ThreadDemo implements Runnable {
static Object obj = new Object();// 定义一个锁

public void run() {
synchronized (ThreadDemo.obj) {

try {

System.out.println("线程进入等待");

obj.wait();// 当执行到这里,线程就进入了等待。等待被另外一个线程所唤醒

System.out.println("线程已经被唤醒");

} catch (InterruptedException e) {
e.printStackTrace();
}

}
}
}

public class Demo {
public static void main(String[] args) throws Exception {
new Thread(new ThreadDemo()).start();// 开始线程

Thread.sleep(3000);// 让程序暂停3秒再往下执行

synchronized (ThreadDemo.obj) {// 代码1
// 代码2
ThreadDemo.obj.notifyAll();// 唤醒了ThreadDemo类中正在等待的线程
}// 代码3

// 如果将以上的代码1,2,3注释,将代码改为如下代码,会发生异常。因为等待唤醒机制必须定义在同步中。
// ThreadDemo.obj.notifyAll();

// 如果将以上的代码1,2,3注释,将代码改为如下代码,会发生异常。因为等待唤醒机制必须使用同一个锁
// synchronized (Demo.class) {
// ThreadDemo.obj.notifyAll();
// }
}
}
/*
* 由此实例可知。 当一个线程进入等待时,线程就不再往下执行了。 如果想往下执行,必须在另外一个线程中将等待的线程给唤醒,否走线程会一直等待下去
*
* 等待唤醒机制的前提: 必须使用在同步中,并且使用的是同一个锁。 同步可以使用synchronized的方法,也可以使用Lock的方式加锁
*/