JAVA_SE总结篇
Java基础知识,值得一遍又一遍过,不知何时起,总是感觉熟悉东西,随着看的次数变多,感觉总会有山重水复疑无路,柳暗花明又一村
的感觉,万丈高楼平地起,对于Java基础应该时长温新一下知识点,这样才能不害怕突然起来机会丧失掉,总是每次都感觉自己面试的时候欠缺很多的,但是又不知道从哪里补充,欠缺,这样的感觉实在不爽,这里开一个复盘系列,先针对基础进行复盘,其中包含很多很基础的概念.这里不包括集合篇,集合篇会单独出专题写;
1.Java执行流程
2. 基本数据类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-noo41jvh-1573120594165)(/Users/gaoxu/Desktop/interview/基本数据类型.jpg)]
高频区间的数据缓存:
public static void main(String[] args) {
// Integer 高频区缓存范围 -128~127
Integer num1 = 127;
Integer num2 = 127;
// Integer 取值 127 == 结果为 true(值127 num1==num2 => true)
System.out.println("值127 num1==num2 => " + (num1 == num2));
Integer num3 = 128;
Integer num4 = 128;
// Integer 取值 128 == 结果为 false(值128 num3==num4 => false)
System.out.println("值128 num3==num4 => " + (num3 == num4));
}
TIPS:Float和Double不会有数据缓存;其他包装类都会有缓存,其中Integer
可以设置缓存的最大值;
-XX:AutoBoxCacheMax=456 即修改缓存最大值为 666
基本数据类型在JVM中的位置:
1.当基本数据类型为局部变量的时候,比如在方法中声明的变量,则存放在方法栈中的,当方法结束系统会释放方法栈,在该方法中的变量也会随着栈的销毁而结束,这也是局部变量只能在方法中使用的原因;
2.基本数据类型为全局变量的时候,比如类中的声明的变量,则存储在堆上,因为全局变量不会随着某个方法的执行结束而销毁。
3.String
特性:
1.String 是标准的不可变类(immutable),对它的任何改动,其实就是创建了一个新对象,再把引用指向该对象;
2.String 对象赋值之后就会在常量池中缓存,如果下次创建会判定常量池是否已经有缓存对象,如果有的话直接返回该引用给创建者;
JVM特殊处理:
String str = "hello," + "world;
String str2 = "hello,world";
System.out.println(str == str2);
结果:true
常用方法:
// 以指定字符串开始
public boolean startsWith(String prefix){};
// 以指定字符串结束
public boolean endsWith(String suffix){};
// 截取字符串,不改变原字符串
public String substring(int beginIndex){};
// 字符串格式化
public static String format(String format, Object... args) {};
比较:equals
比较,已经被覆写Object
对象中的方法了;
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
//比较字符串长度
int n = value.length;
if (n == anotherString.value.length) {
//字符串转化成字符数组
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
// 循环比较两个字符数组中每一个字符是否相等
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
String
,StringBulider
和StringBuffer
与之间区别:
StringBuilder与StringBuffer都是可变字符串.
StringBulider是线程不安全,反之StringBuffer是线程安全;
==
与equals
之间的区别:
本质上equals就是==,只不过很多equals方法都已经被重写了;
总结:
1."=="对于基本类型来说是值比较,对于引用类型来说是比较的是引用;
2.equals默认情况下是引用比较,只是很多类重写了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。
String.intern()
方法:
public native String intern();
intern() 方法用于查找常量池中是否存在该字符值,如果常量池中不存在则先在常量池中创建,如果已经存在则直接返回;
4.运算符
++i 与i++之间的区别:
int i = 0;
int i2 = i++;
int j = 0;
int j2 = ++j;
System.out.println("i2=" + i2); // i2 = 0 先赋值后自加
System.out.println("j2=" + j2); // j2 = 1 先自加再赋值
线程不安全,多线程下并不安全;
三元运算符:
String s = 3 > 1 ? "三大于一" : "三小于一";
System.out.println(s);
5. 异常
1.Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过Error的子类描述。
2.Exception(异常):是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。RuntimeException 异常由Java虚拟机抛出。NullPointerException(要访问的变量没有引用任何对象时,抛出该异常)、ArithmeticException(算术运算异常,一个整数除以0时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。
异常使用的开销:
try-catch 代码段会产生额外的性能开销,或者换个角度说,它往往会影响 JVM 对代码进行优化,因此建议仅捕获有必要的代码段,尽量不要一个大的 try 包住整段的代码;与此同时,利用异常控制代码流程,也不是一个好主意,远比我们通常意义上的条件语句(if/else、switch)要低效。
Java 每实例化一个 Exception,都会对当时的栈进行快照,这是一个相对比较重的操作。如果发生的非常频繁,这个开销可就不能被忽略了。
6.时间&&日期
获取时间:
Date date = new Date();
System.out.println(date);
Calendar calendar = Calendar.getInstance();
Date time = calendar.getTime();
System.out.println(time);
时间戳:
long ts = new Date().getTime();
System.out.println(ts);
long ts2 = System.currentTimeMillis();
System.out.println(ts2);
long ts3 = Calendar.getInstance().getTimeInMillis();
System.out.println(ts3);
格式化: 线程不安全,会造成结果不对,内存泄露等问题;
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sf.format(new Date())); // output:2019-08-16 21:46:22
字符 | 含义 | 示例 |
---|---|---|
y | 年 | yyyy-2019 |
M | 月 | MM-10 |
d | 月中天数 | dd-10 |
D | 年中的天数 | 283 |
E | 星期几 | 星期四 |
H | 小时数(24小时制) | HH-15 |
h | 小时数(12小时制) | hh-03 |
m | 分钟数 | mm-06 |
s | 秒数 | ss-05 |
Z | 时区 | +0800 |
java8
新增的改变:
- 时间获取:
// 获取日期
LocalDate localDate = LocalDate.now();
System.out.println(localDate); // output:2019-08-16
// 获取时间
LocalTime localTime = LocalTime.now();
System.out.println(localTime); // output:21:09:13.708
// 获取日期和时间
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime); // output:2019-08-16T21:09:13.708
- 获取时间戳
long milli = Instant.now().toEpochMilli(); // 获取当前时间戳(精确到毫秒)
long second = Instant.now().getEpochSecond(); // 获取当前时间戳(精确到秒)
System.out.println(milli); // output:1565932435792
System.out.println(second); // output:1565932435
- 时间格式化
// 时间格式化①
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String timeFormat = dateTimeFormatter.format(LocalDateTime.now());
System.out.println(timeFormat); // output:2019-08-16 21:15:43
// 时间格式化②
String timeFormat2 = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(timeFormat2); // output:2019-08-16 21:17:48
- 时间转化
String timeStr = "2019-10-10 06:06:06";
LocalDateTime dateTime = LocalDateTime.parse(timeStr,DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(dateTime);
7.Object类
访问修饰符:
访问级别 | 访问控制修饰符 | 同类 | 同包 | 子类 | 不同的包 |
---|---|---|---|---|---|
公开 | public | ✅ | ✅ | ✅ | ✅ |
受保护 | protect | ✅ | ✅ | ✅ | ❌ |
默认 | 没有修饰符 | ✅ | ✅ | ❌ | ❌ |
私有 | private | ✅ | ❌ | ❌ | ❌ |
常用方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2CkmqhRF-1573120594167)(/Users/gaoxu/Desktop/interview/00_JAVA SE/Object方法.jpg)]
equals():对比两个对象是否相同
getClass():返回一个对象的运行时类
hashCode():返回该对象的哈希码值
toString():返回该对象的字符串描述
wait():使当前的线程等待
notify():唤醒在此对象监视器上等待的单个线程
notifyAll():唤醒在此对象监视器上等待的所有线程
clone():克隆一个新对象
8.内部类
- 成员内部类
class InnerTest {
public static void main(String[] args) {
Outer out = new Outer();
// 创建成员内部类
Outer.Inner inner = out.new Inner();
inner.sayHi();
}
}
class Outer {
public Outer() {
System.out.println("Outer Class.");
}
class Inner {
public void sayHi() {
System.out.println("Hi, Inner.");
}
}
}
创建方式:Outer.Inner inner = new Outer().new Inner();
内部类访问外包类:
class Outer {
private String name = "OuterClass";
public void sayHi() {
System.out.println("Hi, Outer.");
}
class Inner {
public void sayHi() {
// 内部类访问外部类
Outer.this.sayHi();// 访问外部类方法
System.out.println(Outer.this.name);//访问外部类属性
System.out.println("Hi, Inner.");
}
}
}
class InnerTest {
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner();
inner.sayHi();
}
}
访问方式:Outer.this.xxx
其中,xxx代表外部类的方法和属性;
外部类访问内部类:
class Outer {
public void sayHi() {
System.out.println(new Inner().name);// 新建内部类.属性;
System.out.println("Hi, Outer.");
}
private class Inner {
String name = "InnerClass";
public void sayHi() {
System.out.println("Hi, Inner.");
}
}
}
class InnerTest {
public static void main(String[] args) {
new Outer().sayHi();
}
}
总结:
1.成员内部类可直接访问外部类(使用:外部类.this.xxx);
2.外部成员类要访问内部类,必须先建立成员内部类对象;
3.成员内部类可使用任意作用域修饰(public、protected、默认、private);
4.成员内部类可访问外部类任何作用域修饰的属性和方法;
5.外部类建立成员内部类对象之后,可以访问任何作用域修饰的内部类属性和方法。
- 静态内部类
class OuterClass {
public OuterClass() {
System.out.println("OuterClass Init.");
}
// 使用static进行修饰
protected static class InnerClass {
public void sayHi() {
System.out.println("Hi, InnerClass.");
}
}
}
class InnerClassTest {
public static void main(String[] args) {
// 创建内部类的方式
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
innerClass.sayHi();
}
}
TIPS:不能从静态成员内部类中访问非静态外部对象;
- 匿名内部类
interface AnonymityOuter {
void hi();
}
class AnonymityTest {
public static void main(String[] args) {
AnonymityOuter anonymityOuter = new AnonymityOuter() {
@Override
public void hi() {
System.out.println("Hi, AnonymityOuter.");
}
};
anonymityOuter.hi();
}
}
特点:
1.匿名内部类必须继承一个父类或者实现一个接口;
2.匿名内部类不能定义任何静态成员和方法;
3.匿名内部类中的方法不能是抽象的;
9.抽象类和接口
- 抽象类
1.抽象类不能被初始化;
2.抽象类可以有构造方法;
3.抽象类的子类如果为普通类,则必须重写抽象类中的所有抽象方法;
4.抽象类中的方法可以是抽象方法或普通方法;
5.一个类中如果包含了一个抽象方法,这个类必须是抽象类;
6.子类中的抽象方法不能与父类中的抽象方法同名;
7.抽象方法不能为 private、static、final 等关键字修饰;
8.抽象类中可以包含普通成员变量,访问类型可以任意指定,也可以使用静态变量(static);
- 两者区别:
默认方法
1.抽象类可以有默认方法的实现
2.JDK 8 之前接口不能有默认方法的实现,JDK 8 之后接口可以有默认方法的实现
继承方式
1.子类使用 extends 关键字来继承抽象类
2.子类使用 implements 关键字类实现接口
构造器
1.抽象类可以有构造器
2.接口不能有构造器
方法访问修饰符
1.抽象方法可以用 public / protected / default 等修饰符
2.接口默认是 public 访问修饰符,并且不能使用其他修饰符
多继承
1.一个子类只能继承一个抽象类
2.一个子类可以实现多个接口
10.克隆&&序列化
- 浅克隆
class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
Dog dog = new Dog();
dog.name = "旺财";
dog.age = 5;
// 克隆
Dog dog3 = (Dog) dog.clone();
dog3.name = "小白";
dog3.age = 2;
System.out.println(dog.name + "," + dog.age + "岁");
System.out.println(dog3.name + "," + dog3.age + "岁");
}
}
//实现Cloneable接口
class Dog implements Cloneable {
public String name;
public int age;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
浅克隆需要被克隆的对象必须实现Cloneable接口,并且重写clone方法;
存在问题:如果对象中包含引用类型的变量,则不会不复制引用类型;
- 深克隆
方式1:序列化
class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
BirdChild birdChild = new BirdChild();
birdChild.name = "小小鸟";
Bird bird = new Bird();
bird.name = "小鸟";
bird.birdChild = birdChild;
// 使用序列化克隆对象
Bird bird2 = CloneUtils.clone(bird);
bird2.name = "黄雀";
bird2.birdChild.name = "小黄雀";
System.out.println("bird name:" + bird.name);
System.out.println("bird child name:" + bird.birdChild.name);
System.out.println("bird name 2:" + bird2.name);
System.out.println("bird child name 2:" + bird2.birdChild.name);
}
}
class CloneUtils {
public static <T extends Serializable> T clone(T obj) {
T cloneObj = null;
try {
//写入字节流
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bo);
oos.writeObject(obj);
oos.close();
//分配内存,写入原始对象,生成新对象
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());//获取上面的输出字节流
ObjectInputStream oi = new ObjectInputStream(bi);
//返回生成的新对象
cloneObj = (T) oi.readObject();
oi.close();
} catch (Exception e) {
e.printStackTrace();
}
return cloneObj;
}
}
方式2:所有的引用对象也实现克隆
class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ParrotChild parrotChild = new ParrotChild();
parrotChild.name = "小鹦鹉";
Parrot parrot = new Parrot();
parrot.name = "大鹦鹉";
parrot.parrotChild = parrotChild;
// 克隆
Parrot parrot2 = (Parrot) parrot.clone();
parrot2.name = "老鹦鹉";
parrot2.parrotChild.name = "少鹦鹉";
System.out.println("parrot name:" + parrot.name);
System.out.println("parrot child name:" + parrot.parrotChild.name);
System.out.println("parrot name 2:" + parrot2.name);
System.out.println("parrot child name 2:" + parrot2.parrotChild.name);
}
}
class Parrot implements Cloneable {
public String name;
public ParrotChild parrotChild;
@Override
protected Object clone() throws CloneNotSupportedException {
Parrot bird = (Parrot) super.clone();
bird.parrotChild = (ParrotChild) parrotChild.clone();
return bird;
}
}
class ParrotChild implements Cloneable {
public String name;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- 序列化与反序列化
内存中的数据对象只有转换成二进制流才能进行数据持久化或者网络传输,将对象转换成二进制流的过程叫做序列化(Serialization);
相反,把二进制流恢复为数据对象的过程就称之为反序列化(Deserialization)
class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 对象赋值
User user = new User();
user.setName("老王");
user.setAge(30);
System.out.println(user);
// 创建输出流(序列化内容到磁盘)
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.out"));
// 序列化对象
oos.writeObject(user);
oos.flush();
oos.close();
// 创建输入流(从磁盘反序列化)
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.out"));
// 反序列化
User user2 = (User) ois.readObject();
ois.close();
System.out.println(user2);
}
}
class User implements Serializable {
private static final long serialVersionUID = 3831264392873197003L;
private String name;
private int age;
@Override
public String toString() {
return "{name:" + name + ",age:" + age + "}";
}
// setter/getter...
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
总结
简单总结,只是对知识点进行简单罗列,方便以后复盘追溯,主要通过各个点回忆整个技术结构,其实知识点罗列很简单并不深入,需要重新按照点进行对应查看,防止interview的时候知识点抓瞎;
关于我
Hello,我是球小爷,热爱生活,求学七年,工作三载,而今已快入而立之年,如果您觉得对您有帮助那就一切都有价值,赠人玫瑰,手有余香❤️.最后把我最真挚的祝福送给您及其家人,愿众生一生喜悦,一世安康!