考得很细。
1、String问题
//注:字符串常量池从jdk1.7之后已经从方法区,转移到了堆中
String s = new String("123") ;
//一共创建2个对象,在String Pool中创建1个字符串对象指向"123"这个字符串字面量(作为String类的构造函数参数)
//因为是"new"所以自然要在堆中创建1个字符串对象,地址指向s
String s1 = "123";
String s2 = new String("123");
//这时的new创建只创建1个字符串对象,因为在s1 = "123"这一步已经在String Pool中创建了1个123对象
//这时new String(“123”)的构造函数参数"123"则直接用已经放在String Pool的字符串对象,在堆中创建1个对象
//考虑下面的问题
String str1 = new String("ABC");
String str2 = new String("ABC");
System.out.println(str1 == str2);//False.
String str3 = "ABC";
String str4 = "ABC";
String str5 = "A" + "BC";
System.out.println(str3 == str4); //True.
System.out.println(str3 == str5); //True.
String a = "ABC";
String b="AB";
String c=b+"C";
System.out.println(a==c); //false
//字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定。
//不仅如此,还可以使用 String 的 intern() 方法在运行过程将字符串添加到 String Pool 中.
//当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用 equals() 方法进行确定),那么就会返回 String Pool 中字符串的引用;
//否则,就会在 String Pool 中添加一个新的字符串,并返回这个新字符串的引用。
String s3 = new String("aaa");
String s4 = new String("aaa");
System.out.println(s3 == s4); // false
String s5 = s1.intern();
String s6 = s1.intern();
System.out.println(s5 == s6); // true
//练习题(不考虑字符串在常量池中已存在的情况)
String a0="123";//创建1个对象
String a1=new String("123");//2个对象
String a2="123"+"456";//2个
String a3="123"+new String("456"); //4个对象
拼接效率:StringBuilder > StringBuffer > str + “ ”,StringBuilder线程不安全,StringBuffer线程安全
2、Math类方法
//指数函数方法
System.out.println(Math.exp(8));
System.out.println(Math.log(8));
System.out.println(Math.log10(8));
System.out.println(Math.sqrt(8));
//取整函数方法,本次笔试考了这个
System.out.println(Math.ceil(5.2));//6.0
System.out.println(Math.floor(5.2));//5.0
System.out.println(Math.rint(5.2));//5.0
System.out.println(Math.rint(5.6));//6.0rint,个人总结:四舍六入五靠偶
System.out.println(Math.rint(4.5));//4.0,偶数
System.out.println(Math.rint(-4.5));//-4.0偶数
System.out.println(Math.round(5.5f));//6,返回int,参数上加0.5然后进行下取整
System.out.println(Math.round(5.5));//6,long,参数上加0.5然后进行下取整
System.out.println(Math.round(-5.5));//-5
System.out.println(Math.round(-5.6));//-6
//最值、绝对值函数
System.out.println(Math.max(4,8));
System.out.println(Math.min(4,8));
System.out.println(Math.abs(-5.2));
//Math.random()随机数方法
System.out.println(Math.random());//double,0<=double<1
System.out.println(4+(int)(Math.random()*100));//4<=返回<104
System.out.println(new Random().nextInt(104));
//三角函数
System.out.println(Math.sin(Math.PI/2));//1.0
System.out.println(Math.acos(Math.sqrt(2)/2));//0.7853981633974483,即π/4
3、if()括号内赋值问题
boolean flag = false;
if (flag = true){//Test
System.out.println("Test");
}else{
System.out.println("Test2");
}
System.out.println(flag);//true
if (flag = false){//Test4
System.out.println("Test3");
}else {
System.out.println("Test4");
}
System.out.println(flag);//false
//注意:括号内赋值只能是boolean变量
int a;
if(a = 4){//Error: java: 不兼容的类型: int无法转换为boolean
System.out.println("");
}
4、补码与位运算符
//在计算机系统中,数值一律用补码来表示(存储)。计算机一般采用整型数据为32位二进制数
//主要原因:使用补码,可以将符号位和其它位统一处理;
//同时,减法也可按加法来处理。另外,两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。
int i = 0XFFFFFFF1;
System.out.println(i);//-15
System.out.println(~i);//输出为14,~i是对-15的补码0XFFFFFFF1取反
//则得到补码1110,因为符号位变成了0,所以是得到的补码是正数
//正数的补码反码都是其本身,所以1110变为十进制显示就是14
这里顺便复习一下平常很少用的位运算符:
- “按位与”运算:&
- ”按位或“运算:|
- ”按位取反“运算:~
- ”按位异或“运算:^
- ”移位操作“:<<左移(空位补0),>>右移(最高位0空位补0,最高位1空位补1),>>>无符号右移(空位补0)
5、构造函数与静态代码块等执行顺序
执行顺序:(优先级从高到低)静态代码块>mian方法>构造代码块>构造方法
其中静态代码块只执行一次。构造代码块在每次创建对象是都会执行。
普通代码块:在方法或语句中出现的{}就称为普通代码块。普通代码块和一般的语句执行顺序由他们在代码中出现的次序决定–“先出现先执行”
构造代码块
构造代码块:直接在类中定义且没有static修饰没有名字的{}代码块称为构造代码块。
构造代码块在每次创建对象时都会被调用,并且构造代码块的执行次序优先于类构造函数。
- 静态代码块:static修饰
如果结合了继承的问题,则顺序为父类静态代码块———子类静态代码块——父类构造代码块——父类构造方法——子类构造代码块——子类构造方法。
需要注意的是,在实例化子类对象时,父类无参构造方法将被自动调用,但有参构造方法并不能被自动调用,只能依赖于super关键字显式地调用父类的构造方法。更需要注意的是,平时我们新建一个类,会自动提供一个无参的构造方法,但如果一个类已经存在一个有参构造方法,则系统不会再隐式为我们提供无参构造方法。
6、内部类问题
内部类可分为成员内部类、局部内部类、匿名内部类。
6.1、成员内部类
- 内部类可以直接使用外部类中的成员方法和成员变量
- 内部类实例一定要绑定在外部类实例上,如果从外部类中初始化一个内部类对象,则完成了绑定。
- 初始化方式和其他相同,都是用new关键字
- 成员内部类不能存在任何static变量和方法
- 依赖于外部类对象,没有外部类对象就没有内部类对象
- 内部类可以向上转型为接口
private class InnerClass implements OutInterface{}
- this获取内部类和外部类的引用
public class TheSameName{
private int x;
private class Inner{
private int x = 9;
public void doIt(int x){
x++;//形参x
this.x++;//内部类变量x
TheSameName.this.x++;//外部类变量x
}
}
}
6.2、局部内部类
在类的局部位置定义,如在类的方法或任意的作用域中。
- 被定义在方法内部后,不能在方法外部访问该内部类
- 可以访问当前代码块的常量和此外部类的所有成员
- 方法参数设置为final类型
6.3、匿名内部类
- 实质:创建的是继承了类或实现了接口的子类匿名对象
- 前提条件:必须继承一个父类或实现一个接口
- 没有名称,所以使用默认构造方法类生产对象,记住匿名类定义结束后要加分号;
- 编译后产生以“外部类$序号”为名称的.class文件,序号以1~n排列,分别代表n个匿名内部类。
- 最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口
6.4、静态内部类
在内部类前添加static,就变成静态内部类了。
- 静态类对象创建不需要外部类对象
- 静态内部类不能访问外部类的非静态的变量和方法
public class OuterClass {
class InnerClass {
}
static class StaticInnerClass {
}
public static void main(String[] args) {
// InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context
OuterClass outerClass = new OuterClass();
InnerClass innerClass = outerClass.new InnerClass();
StaticInnerClass staticInnerClass = new StaticInnerClass();
}
}
7、static变量及方法访问方式
在Java中,访问static变量和方法有两种方式:
1 通过一个对象去访问,就像访问非静态变量和方法一样
2 通过类去访问,这是常用方式
public class StaticTest {
private static String a;
private static void doStatic(){
System.out.println("静态方法");
}
public static void main(String[] args) {
System.out.println(StaticTest.a);
StaticTest.doStatic();//方式1
StaticTest staticTest = new StaticTest();
staticTest.doStatic();//方式2
StaticTest staticTest1;
staticTest.doStatic();//方式3
}
}
结果:
我们都知道方式1的使用是正确的,但考试会经常考方式2方式3,问你能不能编译成功,然后如果编译成功会不会抛出异常。
8、Java内存回收
我还没有系统学习JVM虚拟机,所以对这方面的知识了解很少。但笔试又经常考这个的选择题,又苦于现在没时间。于是决定每天学一点,抽出睡前和吃饭时间看书学习。
现在转载一下这个博主写的文章。
Java垃圾回收–强引用、软引用、弱引用、虚引用 详细
当程序员通过new关键字创建对象,即视为为Java对象申请内存空间。JVM会在堆内存中为每个对象分配空间;当一个对象失去引用的时候,JVM垃圾回收机制会自动清除他们,并回收他们所占用的内存空间。
Java内存管理包括内存分配(创建Java对象的时候)和内存回收(回收 Java对象的时候)两个方面。
JVM垃圾回收机制是否回收一个对象的标准:是否有引用变量引用该对象?
9、计算机网络相关
9.1、电子邮件协议
常用的电子邮件协议有SMTP、POP3、IMAP4,它们都隶属于TCP/IP协议簇,默认状态下,分别通过TCP端口25、110和143建立连接。
SMTP的全称是“Simple Mail Transfer Protocol”,即简单邮件传输协议。它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转方式。SMTP 协议属于TCP/IP协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。SMTP 服务器就是遵循SMTP协议的发送邮件服务器。SMTP认证,简单地说就是要求必须在提供了账户名和密码之后才可以登录 SMTP 服务器,这就使得那些垃圾邮件的散播者无可乘之机。增加 SMTP 认证的目的是为了使用户避免受到垃圾邮件的侵扰。 SMTP已是事实上的E-Mail传输的标准。
10、Map相同键名问题
Map map = new HashMap();
map.put("name",null);
map.put("name","123");
System.out.println(map.size());
Map map1 = new HashMap();
map1.put("name","123");
map1.put("name","456");
System.out.println(map1.size());
map.putAll(map1);
System.out.println(map.size());
System.out.println(map.get("name1"));
结论:
- put相同键名的键值对,则新增的值会覆盖原来的值
- get不存在的键,不会报错,会返回null
11、线程同步方式
通过Object的wait和notify
通过Condition的awiat和signal
通过一个阻塞队列
通过两个阻塞队列
通过SynchronousQueue
通过线程池的Callback回调
通过同步辅助类CountDownLatch
通过同步辅助类CyclicBarrier
实现线程同步的几种方式总结
12、设计模式
Refactoring:clean your code
笔试考到了桥接模式和单例模式
jdbc规范使用到主要涉及的设计模式:桥接模式
单例模式(Singleton Pattern)是 Java
中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
我们将创建一个 SingleObject 类。SingleObject 类有它的私有构造函数和本身的一个静态实例。
SingleObject 类提供了一个静态方法,供外界获取它的静态实例。SingletonPatternDemo,我们的演示类使用
SingleObject 类来获取 SingleObject 对象。
单例模式的6种实现方式
//1、懒汉式,线程不安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
//2、懒汉式,线程安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
//3、饿汉式(一般情况下建议使用)
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
//4、双检锁/双重校验锁(DCL,即 double-checked locking)
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
//5、登记式/静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
//枚举
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。