Fork me on GitHub

常见面试题

示例 1 : 面向对象的特征有哪些方面?

封装
​ 最常见的是把属性私有化封装在一个类里面,只能通过方法去访问
继承
​ 子类继承父类,从而继承了父类的方法和属性
抽象
​ 比如一个英雄类,抽象出了name,hp这些属性,使得开发过程中更加易于理解
多态
​ 多态分操作符的多态和类的多态。 类的多态指父类引用指向子类对象,并且有继承,有重写。

示例 2 : String是最基本的数据类型吗?

String是类类型,不是基本类型。
基本类型 有八种
这八种基本类型分别是:
整型 (4种)
字符型 (1种)
浮点型 (2种)
布尔型(1种)

示例 3 : int 和 Integer 有什么区别?

int 是基本类型32位长度的整数

Integer 是类类型,是int的封装类

int和Integer之间可以通过自动装箱 自动拆箱 互相转换

示例 4 : String 和StringBuffer的区别?

String是immutable的,其内容一旦创建好之后,就不可以发生改变。
StringBuffer 是可以变长的,内容也可以发生改变
改变的原理是StringBuffer内部采用了字符数组存放数据,在需要增加长度的时候,创建新的数组,并且把原来的数据复制到新的数组这样的办法来实现。

更多细节可以参考 模仿StringBuffer的 MyStringBuffer 类是如何实现的。

阅读更多...

HTTP总结

本文来自 嗨码歌

HTTP协议是什么?

HTTP协议即超文本传输协议,是应用层协议,原是提供一种发布和接受 HTML页面的方法。底层基于TCP协议。发起请求方称为客户端,接收处理请求方称为服务端。

工作步骤 —- 共5步

1)客户端与服务端建立连接

2)客户端发起请求

3)服务器给出响应

4)断开连接

5)浏览器解析响应信息

**特点 **

1)请求/应答

2)安全稳定

3)无状态

请求报文

1)报文格式是什么?

2)浏览器如何查看

响应报文

1)报文格式是什么?

2)浏览器如何查看

阅读更多...

使用Java实现单链表

原文链接 数据结构(一) 单链表的实现-JAVA

一、单链表的概念

链表是最基本的数据结构,其存储的你原理图如下图所示

mark

上面展示的是一个单链表的存储原理图,简单易懂,head为头节点,他不存放任何的数据,只是充当一个指向链表中真正存放数据的第一个节点的作用,而每个节点中都有一个next引用,指向下一个节点,就这样一节一节往下面记录,直到最后一个节点,其中的next指向null。

链表有很多种,比如单链表,双链表等等。我们就对单链表进行学习,其他的懂了原理其实是一样的。

二、用java实现单链表

语言只是一种工具,数据结构真正体会的是那种思想,这句话确实是这样,不管用什么写,其思想是不改变的。以前使用的是C++,现在用的是java,一步步来实现。

###2.1、编写一个Node类来充当结点的模型。我们知道,其中有两个属性,1存放数据的data,2存放下一结点的引用,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.wuhao.demo01;

public class Node {
//为了方便,这两个变量都使用public,而不用private就不需要编写get、set方法了。
//存放数据的变量,简单点,直接为int型
public int data;
//存放结点的变量,默认为null
public Node next;

//构造方法,在构造时就能够给data赋值
public Node(int data){
this.data = data;
}
}

2.2、单链表的简单操作(增加,删除,获取总长度,链表元素排序,链表遍历)

mark

阅读更多...

图 解 Java

这篇文章来自微信公众号 图解Java

一图胜千言。如果图解没有阐明问题,那么你可以借助它的标题来一窥究竟。

1、字符串不变性

下面这张图展示了这段代码做了什么

1
2
String s = "abcd";
s = s.concat("ef");

mark

2、equals()方法、hashCode()方法的区别

HashCode被设计用来提高性能。equals()方法与hashCode()方法的区别在于:

  1. 如果两个对象相等(equal),那么他们一定有相同的哈希值。
  2. 如果两个对象的哈希值相同,但他们未必相等(equal)。

mark

3、Java异常类的层次结构

图中红色部分为受检查异常。它们必须被捕获,或者在函数中声明为抛出该异常。

mark

阅读更多...

自己动手写一个ArrayList

参考视频 自己动手写一个ArrayList吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import java.util.ArrayList;
import java.util.List;

/**
* @author GaoHangHang
* @date 2018/07/16 17:36
**/
/*
泛型generic
java的泛型只存在于编译期,在运行期runtime会被擦除为Object
*/
public class StartArrayList<E>{

/* private E object;

public void setObject(E Object) {
this.object = object;
}*/

private Object[] array;
private final int DEFAULT_SIZE = 20;
private final double RESIZE_RATE = 1.5;
private int size;

//无参数构造方法
public StartArrayList() {
array = new Object[DEFAULT_SIZE];
}

//有参数构造方法
public StartArrayList(int capacity) {
array = new Object[capacity];
}

//得到当前元素总数
public int size() {
return this.size;
}

//但会该arraylsit是否为空
public boolean isEmpty() {
return this.size == 0;
}

//核心的方法,用于数组的扩容,实现动态数组的功能
public void ensureCapacity() {
Object[] old = this.array;
this.array = new Object[(int) (old.length*RESIZE_RATE)];
//iterate through the old array
for (int i = 0; i < old.length; i++) {
array[i] = old[i];
}
}

//尾部添加数据
public boolean add(E data) {
//要对参数进行一个判断
if (data == null)
return false;
//如果已经添加到了最后一个,那么就要扩容
if (this.size == array.length)
ensureCapacity();
array[this.size] = data;
this.size++;

return true;
}

//不是尾部添加,而是在中间怎么办?
public boolean add(E data,int index) {
if (index < 0 || index >=this.size || data == null)
return false;
if (this.size == array.length)
ensureCapacity();
for (int i = this.size; i >= index; i--) {
array[i+1] = array[i];
}
array[index] = data;
this.size++;

return true;
}

//remove 同样有两种情况,第一种根据index来移除,第二种是根据内容来移除(equals)
public E remove(int index) {
if (index <0 || index>= this.size)
//非法参数异常
//throw new IllegalArgumentException("ddd");
return null;
E data = (E) array[index]; //原本取出的是object
for (int i = index; i < this.size-1; i++) {
array[i] = array[i+1];
}

this.size--;
return data;
}

public boolean remove(E data) {
if (data == null)
return false;
for (int i = 0; i < this.size; i++) {
if (array[i].equals(data)){
remove(i);
return true;
}
}
return false;
}

public boolean set(E data, int index){
if (index<0 || index >= this.size)
return false;
array[index] = data;
return true;
}

public E get(int index){
if (index <0 || index >= this.size)
return null;
return (E) array[index];
}

//返回传入元素在arraylist里面的位置
public int indexOf(E data) {
int index = 0;
for (int i = 0; i < this.size; i++) {
if (array[i].equals(data))
return i;
}
return -1;
}

public static void main(String[] args) {
StartArrayList<Integer> list = new StartArrayList<>();
System.out.println(list.size);
list.add(20);
list.add(30);
list.remove(20);
System.out.println(list.size);
list.set(40, 1);
System.out.println(list.get(1));
}
}

ArrayList特点

  • ArrayList自己实现了序列化和反序列化的方法,因为它自己实现了 private void writeObject(java.io.ObjectOutputStream s)和 private void readObject(java.io.ObjectInputStream s) 方法

  • ArrayList基于数组方式实现,无容量的限制(会扩容)

  • 添加元素时可能要扩容(所以最好预判一下),删除元素时不会减少容量(若希望减少容量,trimToSize()),删除元素时,将删除掉的位置元素置为null,下次gc就会回收这些元素所占的内存空间

  • 线程不安全

  • add(int index, E element):添加元素到数组中指定位置的时候,需要将该位置及其后边所有的元素都整块向后复制一位

  • get(int index):获取指定位置上的元素时,可以通过索引直接获取(O(1))

  • remove(Object o)需要遍历数组

  • remove(int index)不需要遍历数组,只需判断index是否符合条件即可,效率比remove(Object o)高

  • contains(E)需要遍历数组

  • 使用iterator遍历可能会引发多线程异常

RandomAccess 这个空架子有何用?

原文链接 RandomAccess 这个空架子有何用?

在学习 Java 集合时, 最先学习的便是 List 中的 ArrayListLinkedList, 学习集合很关键的是学习其源码, 了解底层实现方式, 那么今天就讲讲 ArrayList 实现的一个接口 RandomAccess

好奇心的产生

查看 ArrayList 的源码, 发现它实现了 RandomAccess 这个接口, 出于好奇点进去看看, 结果发现这接口是空的, 这当然引发了更大的好奇心:这空架子到底有何用?

img

深入探究

JDK 官方文档是不可少的工具, 先看看它是怎么说的:RandomAccessList 实现所使用的标记接口,用来表明其支持快速(通常是固定时间)随机访问。此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。

标记接口(Marker):这就说明了 RandomAccess 为空的原因,这个接口的功能仅仅起到标记的作用。

这不是与序列化接口 Serializable 差不多吗? 只要你认真观察, 其实不只这一个标记接口, 实际上 ArrayList 还实现了另外两个这样的空接口:

阅读更多...

数组和链表的区别

数组支持随机访问,查询速度快,增删元素慢;

链表支持顺序访问,查询速度慢,增删元素快。

所以ArrayList查询速度快,增删元素慢,LinkedList查询速度慢

随机访问列表使用循环遍历,顺序访问列表使用迭代器遍历。

ArrayList使用循环遍历,ArrayList使用迭代器遍历。

Volatile关键字

mark

主要内容

  • 什么是volatile?
    • volatile关键词是作为变量的修饰符存在的,它的存在目的是给线程同步提供了除了锁以外的另一种方式。
  • 为什么要使用volatile?
    • 在某些场景下使用volatile关键词去做线程同步在效率是确实要优于锁,并且在某些场景下我们为了避免程序重新排序导致线程读取值的不一致问题,此时也可以考虑使用volatile。
  • volatile实际应用场景
    • 需求1:某线程在运行过程中,需要改变运行状态中的标识位,是否可以通过线程对象将标识位改变?
    • 需求2:启动4个线程对于线程中变量count累加1000次,如何保证启动之后累加结果4000?
阅读更多...

泛型和泛型擦除

泛型是 JDK1.5 的一个新特性,其实就是一个『语法糖』,本质上就是编译器为了提供更好的可读性而提供的一种小「手段」,虚拟机层面是不存在所谓『泛型』的概念的。

在我看来,『泛型』的存在具有以下两点意义,这也是它被设计出来的初衷。

一是,通过泛型的语法定义,编译器可以在编译期提供一定的类型安全检查,过滤掉大部分因为类型不符而导致的运行时异常,例如:

ArrayList list = new ArrayList<>();

list.add(“ddddd”); //编译失败

由于我们的 ArrayList 是符合泛型语法定义的容器,所以你可以在实例化的时候指定一个类型,限定该容器只能容纳 Integer 类型的元素。而如果你强行添加其他类型的元素进入,那么编译器是不会通过的。

二是,泛型可以让程序代码的可读性更高,并且由于本身只是一个语法糖,所以对于 JVM 运行时的性能是没有任何影响的。

当然,『泛型』也有它与身俱来的一些缺点,虽然看起来好像只是提供了一种类型安全检查的功能,但是实际上这种语法糖的实现却没有看起来的那样轻松,理解好泛型的基本原理将有助于你理解各类容器集合框架。

类型擦除

『类型擦除』的概念放在最开始进行介绍是为了方便大家初步建立起对于『泛型』的一个基本认识,从而对于后续介绍的使用方式上会更容易理解。

泛型这种语法糖,编译器会在编译期间「擦除」泛型语法并相应的做出一些类型转换动作。例如:

1
2
3
4
5
public class Caculate<T> {

private T num;

}

我们定义了一个泛型类,具体定义泛型类的细节待会会进行详细介绍,这里关注我们的类型擦除过程。定义了一个属性成员,该成员的类型是一个泛型类型,这个 T 具体是什么类型,我们也不知道,它只是用于限定类型的。

当然,我们也可以反编译一下这个 Caculate 类:

1
2
3
4
5
6
7
public class Caculate{

public Caculate(){}

private Object num;

}

会得到这样一个结果,很明显的是,编译器擦除 Caculate 类后面的两个尖括号,并且将 num 的类型定义为 Object 类型。

阅读更多...
  • Copyrights © 2015-2023 高行行
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信