Silent Majority


  • 首页

  • 关于

  • 归档

Java新版本特性

发表于 2017-08-20

归纳一些值得在面试时回答的Java新特性的点

JDK 7

http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html

Strings in switch Statements

In the JDK 7 release, you can use a String object in the expression of a switch statement:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) {
String typeOfDay;
switch (dayOfWeekArg) {
case "Monday":
typeOfDay = "Start of work week";
break;
case "Tuesday":
case "Wednesday":
case "Thursday":
typeOfDay = "Midweek";
break;
case "Friday":
typeOfDay = "End of work week";
break;
case "Saturday":
case "Sunday":
typeOfDay = "Weekend";
break;
default:
throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg);
}
return typeOfDay;
}

The switch statement compares the String object in its expression with the expressions associated with each case label as if it were using the String.equals method; consequently, the comparison of String objects in switch statements is case sensitive. The Java compiler generates generally more efficient bytecode from switch statements that use String objects than from chained if-then-else statements.

Type Inference for Generic Instance Creation

You can replace the type arguments required to invoke the constructor of a generic class with an empty set of type parameters (<>) as long as the compiler can infer the type arguments from the context. This pair of angle brackets is informally called the diamond.

For example, consider the following variable declaration:

1
Map<String, List<String>> myMap = new HashMap<String, List<String>>();

In Java SE 7, you can substitute the parameterized type of the constructor with an empty set of type parameters (<>):

1
Map<String, List<String>> myMap = new HashMap<>();

Binary Literals

In Java SE 7, the integral types (byte, short, int, and long) can also be expressed using the binary number system. To specify a binary literal, add the prefix 0b or 0B to the number. The following examples show binary literals:

1
2
3
4
5
6
7
8
9
10
11
12
13
// An 8-bit 'byte' value:
byte aByte = (byte)0b00100001;
// A 16-bit 'short' value:
short aShort = (short)0b1010000101000101;
// Some 32-bit 'int' values:
int anInt1 = 0b10100001010001011010000101000101;
int anInt2 = 0b101;
int anInt3 = 0B101; // The B can be upper or lower case.
// A 64-bit 'long' value. Note the "L" suffix:
long aLong = 0b1010000101000101101000010100010110100001010001011010000101000101L;

Underscores in Numeric Literals

In Java SE 7 and later, any number of underscore characters (_) can appear anywhere between digits in a numerical literal. This feature enables you, for example, to separate groups of digits in numeric literals, which can improve the readability of your code.

For instance, if your code contains numbers with many digits, you can use an underscore character to separate digits in groups of three, similar to how you would use a punctuation mark like a comma, or a space, as a separator.

The following example shows other ways you can use the underscore in numeric literals:

1
2
3
4
5
6
7
8
long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi = 3.14_15F;
long hexBytes = 0xFF_EC_DE_5E;
long hexWords = 0xCAFE_BABE;
long maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
long bytes = 0b11010010_01101001_10010100_10010010;

Garbage-First Collector

The Garbage-First (G1) garbage collector is fully supported in Oracle JDK 7 update 4 and later releases. The G1 collector is a server-style garbage collector, targeted for multi-processor machines with large memories. It meets garbage collection (GC) pause time goals with high probability, while achieving high throughput. Whole-heap operations, such as global marking, are performed concurrently with the application threads. This prevents interruptions proportional to heap or live-data size.

JDK 8

http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html

Lambda Expressions

Lambda Expressions, a new language feature, has been introduced in this release. They enable you to treat functionality as a method argument, or code as data. Lambda expressions let you express instances of single-method interfaces (referred to as functional interfaces) more compactly.

Default Methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface TimeClient {
void setTime(int hour, int minute, int second);
void setDate(int day, int month, int year);
void setDateAndTime(int day, int month, int year,
int hour, int minute, int second);
LocalDateTime getLocalDateTime();
static ZoneId getZoneId (String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println("Invalid time zone: " + zoneString +
"; using default time zone instead.");
return ZoneId.systemDefault();
}
}
default ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
}
}

Suppose that you want to add new functionality to the TimeClient interface, such as the ability to specify a time zone through a ZonedDateTime object (which is like a LocalDateTime object except that it stores time zone information).

Following this modification to the TimeClient interface, you would also have to modify the class SimpleTimeClient and implement the method getZonedDateTime. However, rather than leaving getZonedDateTime as abstract (as in the previous example), you can instead define a default implementation. (Remember that an abstract method is a method declared without an implementation.)

Java Date-Time Packages

  • java.time - Classes for date, time, date and time combined, time zones, instants, duration, and clocks.

补充 HashMap & ConcurrentHashMap

https://zhuanlan.zhihu.com/p/21673805

数组 + 链表 -> Node数组+链表+红黑树(TreeNode)

链表长度超过8时,链表转换为红黑树

锁分段 -> CAS算法 + synchronized(put操作hash值相同节点)

CAS三个操作数:内存值,期待值,新值。如果内存值和期待值相匹配,更新为新值;否则不执行操作。

补充 移除永久代

引入元空间Metaspace

总结

JDK 7

  • switch支持String类型
  • 泛型类型指定简化
  • 二进制表示
  • 数字中下划线
  • G1垃圾收集器

JDK 8

  • Lambda表达式
  • 接口中默认方法
  • 新的时间类

深入理解Java虚拟机:JVM高级特性与最佳实践 笔记

发表于 2017-08-13

深入理解Java虚拟机:JVM高级特性与最佳实践

第2章 Java内存区域与内存溢出异常

运行时数据区域

  • 程序计数器

    当前线程所执行的字节码的行号指示器,线程私有

  • Java虚拟机栈

    线程私有,描述Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储信息(局部变量表等)

    局部变量表存放了各种基本数据类型、对象引用类型。

    • 线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverflowError异常
    • 虚拟机扩展时无法申请到足够的内存,抛出OutOfMemoryErro异常

    虚拟机栈为虚拟机执行Java方法(字节码)服务

  • 本地方法栈

    本地方法栈为虚拟机使用到的Native方法服务

  • Java堆

    所有线程共享,存放对象实例,垃圾收集器管理的主要区域

    新生代 : 老年代 = 1:2

    • 新生代(1/3堆空间)

      Eden区 : from Survivor : to Survivor = 8:1:1

      • Eden (8/10新生代空间)
      • From Survivor (1/10新生代空间)
      • To Survivor (8/10新生代空间)
    • 老年代(2/3堆空间)

  • 方法区

    线程共享

    • 运行时常量池

    永久区从jdk7开始逐步移除,在jdk8中存储在元空间中

第3章 垃圾收集器与内存分配策略

  • 哪些内存需要回收
  • 什么时候回收
  • 怎么回收

判断对象是否存活

  • 引用计数算法

    很难解决对象之间相互循环引用的问题

  • 可达性分析算法

    通过一系列GC Roots对象作为起始点,从这些节点开始向下搜索。从GC Roots到某个对象不可达时,证明此对象不可达

引用

  • 强引用

    Object obj = new Object() 强引用存在,不会被垃圾收集器回收

  • 软引用

    发生内存溢出异常之前,被列进回收范围

  • 弱引用

    无论当前内存是否足够,垃圾收集器都会回收

  • 虚引用

    最弱的引用关系

宣告对象死亡

如果对象进行可达性分析后发现不可达,被第一次标记并进行一次筛选。筛选条件为此对象是否有必要执行finalize()方法。对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用过,视为没有必要执行。

如果对象被判定有必要执行finalize()方法,对象被放置在队列中,稍后被执行。”执行”指虚拟机触发这个方法,但并不承诺等待它运行结束。

如果对象重新与引用链上任何一个对象建立关联,第二次标记时将被移除出即将回收的集合;否则被真的回收。

垃圾收集算法

  • 标记-清除算法

    最基础的收集算法

    不足

    • 标记和清楚两个过程效率都不高
    • 产生大量不连续的内存碎片,导致分配较大对象时提前出发另一次垃圾收集动作
  • 复制算法

    内存按容量划分为大小相等的两块,每次只是用其中的一块。当一块内存用完,将还存活的对象复制到另外一块上面,再把已使用过的内存空间一次清楚掉。每次都是对整个半区进行内存回收。

    缺点:内存缩小为原来的一半

    新生代对象大多数生命周期短,所以将内存分为一块较大的Eden空间和两块较小的Survivor空间。每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活的对象一次性地复制到另外一块Survivor空间上,最后清楚掉Eden和刚才使用过的Survivor空间。

    虚拟机默认Eden和Survivor大小比例是8:1。

    Survivor区域有两块的原因:避免只有一块survivor区域中垃圾回收后产生碎片化

  • 标记-整理算法

    标记与标记-清除相同,后续让所有的对象都向一端移动,然后直接清理掉端边界以外的内存

  • 分代收集算法

    • 新生代:只有少量对象存活,选用复制算法
    • 老年代:对象存活率高,使用标记-清除或者标记-整理

垃圾收集器

  • 新生代垃圾收集器

    • Serial收集器

      单线程,进行垃圾收集时,必须暂停其他所有工作线程;复制算法

    • ParNew收集器

      Serial收集器的多线程版本

    • Parallel Scavenge收集器

      可控制的吞吐量

  • 老年代垃圾收集器

    • Serial Old收集器

      Serial收集器的老年代版本,单线程,标记-整理算法

    • Parallel Old收集器

      Parallel Scavenge收集器的老年代版本,多线程,标记-整理算法

  • CMS收集器 Cuncurrent Mask Sweep

    标记-清除算法,目标为获取最短回收停顿时间

    • 初始标记:标记GC Roots能直接关联到的对象,速度快
    • 并发标记:GC Roots Tracing
    • 重新标记:修正并发标记期间因用户程序继续运作而到导致标记产生变动的那一部分对象
    • 并发清除

    初始标记、重新标记需要Stop The World;总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。

    缺点

    • 对CPU资源敏感
    • 无法处理浮动垃圾
    • 大量空间碎片产生
  • G1收集器

    • 并行与并发
    • 分代收集
    • 空间整合:从整体来看基于标记-整理算法,从局部(两个Region之间)来看基于复制算法
    • 可预测的停顿

注意

  • 并行:多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
  • 并发:用户线程与垃圾收集线程同时执行,用户程序在继续运行,而垃圾收集程序运行于另一个CPU上

内存分配与回收策略

  • 对象优先在新生代Eden区分配,Eden区没有足够空间分配时,虚拟机发起一次Minor GC

  • 大对象(需要大量连续内存空间的Java对象)直接进入老年代

  • 长期存活的对象进入老年代

    对象在Survivor区每次经过一次Minor GC,年龄增加一岁。增加到一定程度(默认15岁),晋升到老年代

  • 动态对象年龄判断

    Survivor空间中相同年龄所有对象大小总和大于Survivor空间的一半,年龄大于或者等于该年龄的对象可以直接进入老年代

  • 空间分配担保

第7章 虚拟机类加载机制

虚拟机的类加载机制:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型

类加载的过程

  • 加载

    通过一个类的全限定名获取定义此类的二进制字节流,将这个字节流所代表的静态存储结构转换成方法区的运行时数据结构,在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

  • 验证

    确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全

  • 准备

    正式为类变量(被static修饰的变量)在方法区中分配内存并设置类变量的初始值(基本数据类型的零值,ConstantValue除外)

  • 解析

    虚拟机将常量池内的符号引用替换为直接引用

  • 初始化

    开始执行类中定义的Java程序代码(字节码)

类加载器

双亲委派模型

三种系统提供的类加载器

  • 启动类加载器:C++语言实现,是虚拟机自身的一部分
  • 拓展类加载器:\lib\ext,开发者可以直接使用扩展类加载器
  • 应用程序类加载器

类加载器之间的层次关系称为类加载器的双亲委派模型。双亲委派模型要求除了顶层的启动类加载器外,其余的类都应当有自己的父亲加载器。

双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父亲加载器去完成。所有的加载请求最终应该传送到顶层的启动类加载器中。只有当父加载器反馈自己无法完成这个加载请求时,子类才会尝试自己去加载。

破坏双亲委派模型

  • 重写findClass()方法

C++和Java的继承与多态相关

发表于 2017-08-13

概念

面向对象三大特征

  • 封装
  • 继承
  • 多态

多态

多态的实现

  • 方法的重载 编译时多态
  • 方法的覆盖 运行时多态

多态的必要条件

  • 继承
  • 重写
  • 父类引用指向子类对象

Java

重载

在一个类中,参数个数不同或类型不同或顺序不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A {
public void fun(int num) {
System.out.println("int");
}
public void fun(int num, int num2) {
System.out.println("int int");
}
public void fun(double num) {
System.out.println("double");
}
}
public class Main {
public static void main(String[] args){
A a = new A();
a.fun(1);
a.fun(1,2);
a.fun(1.0);
}
}
1
2
3
int
int int
double

重写

父类与子类中,相同函数名和参数,相同返回值类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class B {
public void fun() {
System.out.println("Father");
}
}
class D extends B{
@Override
public void fun() {
System.out.println("Child");
}
}
public class Main {
public static void main(String[] args){
B b = new B(); b.fun();
D d = new D(); d.fun();
B dd = new D(); dd.fun();
}
}
1
2
3
Father
Child
Child

隐藏

父类的静态方法被子类的同名静态方法隐藏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class B {
static public void fun() {
System.out.println("Father");
}
}
class D extends B{
static public void fun() {
System.out.println("Child");
}
}
public class Main {
public static void main(String[] args){
B b = new B(); b.fun();
D d = new D(); d.fun();
B dd = new D(); dd.fun(); //pay attention!
}
}
1
2
3
Father
Child
Father

更好的写法

1
2
B.fun();
D.fun();

初始化顺序

  1. 静态对象(变量)优先于非静态对象(变量)。静态对象(变量)只能初始化一次。
  2. 父类优先于子类

顺序:父类静态、子类静态、父类非静态、父类构造、子类非静态、子类构造

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
class B {
static {
System.out.println("Father static");
}
{
System.out.println("Father non-static");
}
B() {
System.out.println("Father constructor");
}
}
class D extends B{
static {
System.out.println("Child static");
}
{
System.out.println("Child non-static");
}
D() {
System.out.println("Child constructor");
}
}
public class Main {
public static void main(String[] args){
B d = new D();
System.out.println();
B dd = new D();
}
}
1
2
3
4
5
6
7
8
9
10
11
Father static //父类静态
Child static //子类静态
Father non-static //父类非静态
Father constructor//父类构造
Child non-static //子类非静态
Child constructor //子类构造
Father non-static
Father constructor
Child non-static
Child constructor

C++

父类指针指向子类对象

调用普通函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class B {
public:
void fun() {
cout << "Father" << endl;
}
};
class D:public B{
public:
void fun() {
cout << "Child" << endl;
}
};
int main() {
B *b = new B(); b->fun();
D *d = new D(); d->fun();
B *dd = new D(); dd->fun(); //pay attention
return 0;
}
1
2
3
Father
Child
Father //pay attention

调用函数根据指针类型确定,而不是根据指针实际指向的对象类型确定

调用虚函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class B {
public:
virtual void fun() {
cout << "Father" << endl;
}
};
class D:public B{
public:
virtual void fun() {
cout << "Child" << endl;
}
};
int main() {
B *b = new B(); b->fun();
D *d = new D(); d->fun();
B *dd = new D();dd->fun(); //pay attention
return 0;
}
1
2
3
Father
Child
Child //pay attention

只有通过基类的引用或者指针调用虚函数时,才能发生动态绑定。

腾讯秋招模拟题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Base {
public:
virtual void foo() {cout << "Base foo" << endl;}
void bar() {cout << "Base bar" << endl; foo();}
};
class Derive:public Base {
public:
void foo() {cout << "Derive foo" << endl;}
void bar() {cout << "Derive bar" << endl; foo();}
};
int main() {
Base *ptr = new Derive();
ptr->bar();
}
1
2
Base bar // 静态绑定
Derive foo // virtual修饰,动态绑定

隐藏

如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏。

如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有 virtual关键字。此时,基类的函数被隐藏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class B {
public:
void fun() {
cout << "Father" << endl;
}
};
class D:public B{
public:
void fun(int num) {
cout << "Child" << endl;
}
};
int main() {
D d;
//d.fun(); error
d.fun(0);
return 0;
}

C++ 类的实例化对象大小

发表于 2017-08-13

实习生笔试面试时碰到过多次的问题,通过运行代码记录一下结论。测试平台为64位。

空类

1
2
3
4
5
6
class A {};
int main() {
A a;
cout << "sizeof(a) is " << sizeof(a) << endl;
return 0;
}
1
sizeof(a) is 1

空类的大小为1

只含有构造函数、析构函数、成员函数的类

1
2
3
4
5
6
7
8
9
10
11
class A {
public:
A() {};
~A() {};
void fun() {};
};
int main() {
A a;
cout << "sizeof(a) is " << sizeof(a) << endl;
return 0;
}
1
sizeof(a) is 1

大小与构造函数、析构函数、成员函数无关

含有成员变量的类

需要考虑对齐

C++中数据类型占据字节长度
| 数据类型 | 大小 |
| —— | —- |
| char | 1 |
| bool | 1 |
| short | 2 |
| int | 4 |
| float | 4 |
| double | 8 |

简单类

1
2
3
4
5
6
7
8
9
10
class A {
public:
char ch;
int num;
};
int main() {
A a;
cout << "sizeof(a) is " << sizeof(a) << endl;
return 0;
}
1
sizeof(a) is 8

size = 1(char) + 3(padding) + 4(int) = 8

成员变量不同的顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class A {
public:
double d;
char ch;
int num;
};
class B {
public:
char ch;
double d;
int num;
};
int main() {
A a;
cout << "sizeof(a) is " << sizeof(a) << endl;
B b;
cout << "sizeof(b) is " << sizeof(b) << endl;
return 0;
}
1
2
sizeof(a) is 16
sizeof(b) is 24

大小为占用最大空间的类型所占用的字节数的倍数

sizeof(a) = 8(double) + 1(char) + 3(padding) + 4(int) = 16

sizeof(b) = 1(char) + 7(padding) + 8(double) + 4(int) + 4(padding) = 24

指针

1
2
3
4
5
6
7
8
9
class A {
public:
int* p;
};
int main() {
A a;
cout << "sizeof(a) is " << sizeof(a) << endl;
return 0;
}
1
sizeof(a) is 8

指针变量的大小与机器平台有关,64位机器为8个字节

静态成员变量

1
2
3
4
5
6
7
8
9
10
11
class A {
public:
char ch;
int num;
static int count;
};
int main() {
A a;
cout << "sizeof(a) is " << sizeof(a) << endl;
return 0;
}
1
sizeof(a) is 8

静态成员变量不影响

虚函数

1
2
3
4
5
6
7
8
9
10
class A {
public:
virtual void fun1() {}
virtual void fun2() {}
};
int main() {
A a;
cout << "sizeof(a) is " << sizeof(a) << endl;
return 0;
}
1
sizeof(a) is 8

有虚函数时,占用个内存会包含一个虚表指针(64位大小为8字节),且不论虚函数的个数多少只有一个

虚表指针放在对象内存的起始位置

继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A {
public:
char ch1;
};
class B:public A{
public:
char ch2;
int num;
};
int main() {
A a;
cout << "sizeof(a) is " << sizeof(a) << endl;
B b;
cout << "sizeof(b) is " << sizeof(b) << endl;
return 0;
}
1
2
sizeof(a) is 1
sizeof(b) is 8

大小叠加并考虑对齐,基类成员在前

同名变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A {
public:
int num;
};
class B:public A{
public:
int num;
};
int main() {
A a;
cout << "sizeof(a) is " << sizeof(a) << endl;
B b;
cout << "sizeof(b) is " << sizeof(b) << endl;
return 0;
}
1
2
sizeof(a) is 4
sizeof(b) is 8

含有虚函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A {
public:
virtual void fun1() {}
};
class B:public A{
public:
virtual void fun2() {}
};
int main() {
A a;
cout << "sizeof(a) is " << sizeof(a) << endl;
B b;
cout << "sizeof(b) is " << sizeof(b) << endl;
return 0;
}
1
2
sizeof(a) is 8
sizeof(b) is 8

基类有虚函数,派生类也有虚函数,在虚表指针指向的虚表中添加一个函数指针,所以派生类仍仅有一个虚表指针

虚继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A {
public:
int num;
virtual void fun1() {}
};
class B:virtual public A{
public:
virtual void fun2() {}
};
int main() {
A a;
cout << "sizeof(a) is " << sizeof(a) << endl;
B b;
cout << "sizeof(b) is " << sizeof(b) << endl;
return 0;
}
1
2
sizeof(a) is 16
sizeof(b) is 24

与编译器有关,测试使用的是gcc

sizeof(b) = vptr_b(8) + sizeof(a) = 24

其他编译器还会有子类指向父类的指针

大三寻找暑假实习的总结

发表于 2017-05-12

从三月上旬投递简历,到今天拿到一个还不错的offer,过去了两个月的时间。如果接下来顺利的话,暑假的去向也不会有改变了,所以写一篇博客来记录一下这两个月总结。

  • 阿里

    投递简历,做了性格测试和编程测试的周末3月12日杭州就打来了电话,不过至今都不知道这通电话属于内推还是提前打捞简历池。因为是最早接到的一次电话面试,Java刚刚复习,数据库也没有复习,计算机网络的课程也处于学习阶段,所以有很多问题根本没有回答上来。

    电话流程基本上是自我介绍,对暑假项目的推荐算法进一步问了还有哪些公式计算相似度,Java,数据结构,数据库,网络,框架,计算机体系……很多方面都涉及到了。

    从现在的角度来看,这个电话面试其实是覆盖面最大的一个,当时复习也没有怎么开始,所以不出意外地没有通过,其实这个阿里的面试没有能够进行下去是最为遗憾的一件事情。4月26日做了正式的笔试,选择题部分有很多C++的部分(?),感觉内推已经消耗了大量的岗位,所以也没有报什么希望,至今状态处在笔试中。

  • 华为

    3月17日笔试,三道编程题,整个找实习阶段最简单的一次考试,很轻松地AC了三道。上操作系统课的时候发现漏接一通电话,一查是华为,所以后来整节课都没有好好听。下午接到了HR第二次电话预约了周五面试。

    4月14日面试首先是惯例的自我介绍,然后又问几个常规问题,讲了一下做的项目,因为提问介绍了这个学期做的项目的数据库设计,正好那一周写了需求规约,所以没出什么问题。手写了一个初中学Pascal的时候就学过的算法,不过即使学了也是毫无印象了。考虑了特殊情况后,模拟人工的情况,回来以后发现vector和queue的方法用混了…第二面基本无实质性内容,非常愉悦地回来了。

    因此感觉自己华为能过,所以开始了漫长的等待。第二周接到电话询问是否已经面试。第三周操作系统课一下课就收到了实习时间和地点的询问电话,这个时候感觉差不多稳了。期间加了华为实习的群,天天查看官网的状态,整个过程非常地煎熬。四月底开始,华为各地开始陆续发放offer,然而上海这边每周都会有同学过去面试。知道五月第一周上研差不多结束之后,还发短信问了HR,结果没有回复…

    到了这一周,预计可以发offer,所以到哪里都带着手机(这半个月被吐槽好几次为什么吃个晚饭要带包,之前五一长跑节活动的时候也拿着)。看着讨论群里南京都陆续地发了两三批,心灰意冷地等下周的时候,今天周五终于收到了offer的短信,具体信息还要看下周的邮件和电话,不过这个也是我能拿到的最好的offer了。

  • Pwc 信息技术

    这个是学校职场灯塔推送的,本来以为是律师事务所下的技术部门,不过后来发现是个独立的Pwc子公司。宣讲3月20日和摩根撞了时间,所以交大这里人就不多。糊里糊涂地做了会后的笔试,在周四操作系统课(又是OS课)的时候接到笔试通过邀请去群面的短信,为了正装的要求还折腾了一会。

    3月25日下午群面,翘掉了网易的笔试(不过本来报的上海子公司就放弃了),第一次做无领导小组讨论,所以就做了聆听者。中间提了一个建议让整个团队改变了一些思考策略,又给面试官推销了自己的技术能力,所以尽管遇到了“你认为谁应该被淘汰”这种杀手级问题没有回答出来的窘境,但还是过了。

    3月31日又去了长泰广场一对多终面。等待期间被华师大同学包围,当时群面的小组似乎是进的人最多的。终面依然介绍项目,回答将来学习新技术能接受吗这类问题,期间又推销了一下自己写D3JS可视化的内容。

    4月11日收到offer,和HR在12日沟通,要求马上给出负责的回答,当时正好华为通知去面试,等终面的时候也看到了实际的工作场景,思考了一个晚上,觉得自己还是可以承担0offer的后果拒掉了。

  • 美团点评

    3月21日做的笔试,智力题即找数字规律部分根本不会做,所以笔试也没有通过。

  • 携程

    3月22日学校宣讲会做的笔试,当时为了能够赶上时间还把项目中期答辩的时间提到了第一个。做完后感觉不错,所以等第二周HR打电话。3月28日收到了HR电话,第二天就去了凌空SOHO面试了。

    介绍项目,被质疑了部分内容;回顾笔试算法题目,期间被我弄巧成拙;Java的常规问题,数据类型的位数严重记不清,波及了整场面试;手写算法,题目正好和美团点评的相同;结束后,面试官说还要注重基础…

    后续就石沉大海,做了4月11日的网上笔试,查到笔试通过一周多后在操作系统课上接到电话面试。大概持续了十几分钟,然后又没有下文了…

  • VMWare

    我都不想承认报过这家公司,三道题一道都没有做出来,还是我面完携程特地赶回学校做的测试。

  • 摩根

    同样不想承认,慌慌张张地在笔试卷子有效期截止当晚做了Java和C++两套,没有消息。

  • 微软

    3月31日的四道算法题依然一道未过,调剂去了技术支持。本着先试试想法,4月17日直接从学校去了紫竹。先是写封邮件,然后三面,包括计算机网络、操作系统知识,模拟真实情况以及英语能力测试。第三面主管面讲了好久这个学期项目的教训与改进之处,以及和路由有关的问题。

    觉得回答得也不是很差,但还是收到了Thank You Letter。

  • 腾讯

    3月底接过一个深圳的电面,不过我也没有参加内推呀…本来报的是Web开发岗位,但问了很多JavaScript内容,所以一结束就换成了后端开发。

    4月4日做的网上笔试,网络和C++部分依然不熟。4月16日去酒店一面,腾讯看起来内部使用C++为主,但复习的一直是Java所以第二天就看到“目前职位不太适合”的状态。

  • 百度

    华为面试完回来晚上报的软件开发。4月27日笔试的时候又让我后悔没有看两眼C++,编程部分AC2.3道,不过同样至今没有消息。

总结下来整整两个月就收获了一大一中两家公司的offer,和厉害的人还是不能比啊。希望今天收到的offer之后跟进流程能顺利。

面向实习面试的复习之Java

发表于 2017-04-15

Java C++ 区别

  1. Java解释型:程序经编译器变成成字节码,由JVM解释执行;C++编译型:源代码经编译与链接后生成可执行二进制代码。
  2. Java纯面向对象:除基本类型(int,float),所有类型都是类;C++兼具面向过程与面向对象。
  3. Java无指针概念,程序更安全,避免溢出
  4. Java不支持多重继承,引入了接口概念;C++多重继承。
  5. Java提供了垃圾回收器,调用对象的finalize方法;C++开发人员管理内存分配,包括申请与释放(析构)
  6. Java不支持运算符重载
  7. Java平台无关:int类型32位

Java初始化顺序

  1. 静态优于非静态
  2. 父类优于子类
  3. 成员变量按照顺序初始化

父类静态变量、代码块 ->

子类静态变量、代码块 ->

父类非静态变量、代码块 ->

父类构造函数 ->

子类非静态变量、代码块 ->

子类构造函数

Java作用域

  • 成员变量:类被实例化,成员变量在内存分配空间并初始化,直至实例化对象生命周期结束
  • 静态变量(static):类被加载时
  • 局部变量:花括号内
当前类 本包 子类 其他包
public √ √ √ √
protected √ √ √
default √ √
private √

构造函数

没有编写构造器时,编译器提供一个无参数构造函数(默认值,数值类型-0,布尔-false,对象-null)

子类通过super显式调用父类的构造器

父类没有提供无参数的构造函数时,子类构造器函数必须显式调用父类的构造函数

clone

除基本数据按值传递,对象在函数调用与=赋值时采用引用传递

从已有对象A创建一个相同状态B,且对B修改不影响A,使用clone()

1
2
3
4
5
class Obj implements Cloneable {
public Obj clone() throws CloneNotSupportedException {
return (Obj)super.clone();
}
}

深复制:复制后的对象引用指向新的对象

浅复制:复制后的对象引用仍然指向原来的对象

反射机制

  • 得到一个对象所属的类
  • 获得一个类的所有成员变量和方法
  • 运行时创建对象
  • 运行时调用对象的方法
1
2
3
4
5
6
7
8
9
class Base {……}
class Sub extends Base {……}
try {
Class c = Class.forName("Sub");
Base b = (Base)c.newInstance();
b.f();
} catch (Exception e){
}

面向对象

继承、封装、多态

多态实现机制

  • 方法重载:同一个类中多个同名方法,不同参数(不同参数个数,不同参数类型,不同参数顺序),编译时多态
  • 方法覆盖:子类覆盖父类的方法(函数名参数相同,返回值相同,抛出异常一致,被覆盖方法不能是private),运行时多态

抽象类 extends

  • 只要包含抽象方法的类必须声明为抽象类,被声明抽象的方法不能包含方法体。
  • 抽象类使用中不能被实例化,但可以创建对象指向具体子类的一个实例。
  • 抽象类子类为父类所有抽象方法提供具体实现,否则也是抽象类。

接口 implements

接口中所有方法都是抽象

this super

  • this指向当前实例对象
  • super访问父类方法和成员变量
    • 子类构造函数显示调用父类构造函数时,super必须为构造函数中第一句语句

final

  • final 修饰类 类不能被继承
  • final 修饰方法 方法不能被重写
  • final 修饰变量 值不可被修改 (引用不可变)

static

  • static 成员变量:静态变量,为类的所有对象共享
  • static 成员方法:不需要创建对象可以被调用
  • static 代码块:静态代码块在类被加载时被被调用

volatile

volatile 修饰被不同线程访问和修改的变量,每次使用时从内存提取,而不利用缓存;不能保证原子性

基本数据类型

类型 位长 包装类
int 4 byte / 32 bit Integer
short 2 byte / 16 bit Short
long 8 byte / 64 bit Long
byte 1 byte / 8 bit Byte
float 4 byte / 32 bit Float
double 8 byte / 64 bit Double
char 2 byte / 16 bit Character
boolean 1 byte / 8 bit Boolean

不可变类

  • 基本类型的包装类是不可变类;
  • String是不可变类

字符串

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

内存地址不同

String s1 = "abc";String s2 = "abc";

JVM中字符串池,可以被共享使用。s1,s2引用同一个常量池中对象。
new String(“abc”) 创建了一个或者两个对象:常量池中有”abc”,一个;否则,两个。

== equals hashCode

  • ==:比较基本类型数据或者两个引用变量是否相等
  • equals:String检测两个字符串是否相等
  • hashCode:内存中地址转换成int值

equals true -> hashCode 相同整数

equals false -> hashCode 可能相等,可能不相等

String StringBuilder StringBuffer

  • String:不可变类,String对象一旦被创建,其值不能被改变
  • StringBuilder:线程非安全的
  • StringBuffer:线程安全的,同步

length length()

  • length:获取数组的长度
  • length():计算字符串的长度

try catch finally

try{ }中有return语句,但在return之前会运行finally块里的代码

finally块中有return语句时,会覆盖函数中其他return语句

Java 堆与栈

  • 栈: 基本类型 int, short, long, byte, float, double, boolean, char 以及对象的引用变量
  • 堆: new创建的对象和数组

Collections框架

List, Queue, Set, Stack继承自Collection接口

接口

  • Set : HashSet, TreeSet(有序)
  • List : LinkedList, ArrayList, Vector
  • Map : HashMap(散列表,hashCode), TreeMap(红黑树)

List

  • ArrayList 基于数组 随机访问get与set性能优,线程不安全
  • LinkedList 基于双向列表 新增与删除add与remove性能优,线程不安全
  • Vector 基于数组,线程安全

HashMap Hashtable

HashMap是Hashtable轻量级实现

  • Hashtabl线程安全
  • HashMap线程不安全,最多允许一条键值为null;
    • 添加键值对,调用key的hashCode方法生成hash值,不存在直接添加;
    • 存在,找到所有key,调用equals方法比较,true覆盖,false添加;
    • 冲突,链地址法
  • ConcurrentHashMap

Collection Collections

  • Collection是一个集合接口
  • Collections是针对集合类的一个包装类
1
2
List<Integer> list = new LinkedList<Intger>();
Collections.sort(list);

面向实习面试的复习之排序

发表于 2017-04-14

最近一直在寻找暑假的实习,照道理来说排序应该是一个高频的面试问题,不过至今也只遇到了描述一下快速排序思想的问题,这里参考了大二算法课上老师所讲的算法以及《算法》(第4版)的内容,使用C++做了练习。
如果有错误的话,也欢迎告诉我。(不过博客还没有评价系统..)

冒泡

时间复杂度 O(n2)


1
2
3
4
5
6
void bubble_sort(vector<int> &numbers) {
for (int i = 0; i < numbers.size(); i++)
for (int j = i + 1; j < numbers.size(); j++)
if (numbers[i] < numbers[j])
swap(number[i], numbers[j]);
}


选择

时间复杂度 O(n2)


1
2
3
4
5
6
7
8
void choose_sort(vector<int> &numbers) {
for (int i = 0; i < numbers.size(); i++) {
int min = i;
for (int j = i + 1; j < numbers.size(); j++)
if (numbers[min] > numbers[j]) min = j;
swap(numbers[i], numbers[min]);
}
}


插入

时间复杂度 O(n2)


1
2
3
4
5
6
void insert_sort(vector<int> &numbers) {
for (int i = 1; i < numbers.size(); i++) {
for (int j = i; j > 0 && numbers[j] < numbers[j - 1]; j--)
swap(numbers[j], numbers[j - 1]);
}
}


快排

时间复杂度O(nlogn) 最坏状态 O(n2)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int partition(vector<int> &numbers, int lo, int hi) {
int i = lo; int j = hi + 1;
int n = numbers[lo];
while (true) {
while (numbers[++i] < n) if (i == hi) break;
while (numbers[--j] > n) if (j == lo) break;
if (i >= j) break;
swap(numbers[i], numbers[j]);
}
swap(numbers[lo], numbers[j]);
return j;
}
void quick_sort(vector<int> &numbers, int lo, int hi) {
if (lo >= hi) return;
int j = partition(numbers, lo, hi);
quick_sort(numbers, lo, j - 1);
quick_sort(numbers, j + 1, hi);
}

归并

时间复杂度O(nlogn)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int merge(vector<int> &numbers, int lo, int mid, int hi) {
int i = lo; int j = mid + 1;
vector<int> copy;
copy = numbers;
for (int k = lo; k <= hi; k++) {
if (i > mid) numbers[k] = copy[j++];
else if (j > hi) numbers[k] = copy[i++];
else if (copy[i] < copy[j]) numbers[k] = copy[j++];
else numbers[k] = copy[i++];
}
}
void merge_sort(vector<int> &numbers, int lo, int hi) {
if (lo >= hi) return;
int mid = lo + (hi - lo) / 2;
merge_sort(numbers, lo, mid);
merge_sort(numbers, mid + 1, hi);
merge(numbers, lo, mid, hi);
}

Hello World

发表于 2017-04-10

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

12
Edward

Edward

SE @ SJTU

18 日志
3 标签
© 2018 Edward
由 Hexo 强力驱动
主题 - NexT.Muse