-
- 基础
-
- 基本数据类型(字节)
- JDK 和 JRE
- static
- final 关键字
- finally关键字
- Java 和 C++的区别
- 构造器 Constructor 是否可被 override?
- a+=b 和 a=a+b 有什么区别吗?
- Iterator 和 增强for
- 重载和重写的区别(都是多态的表现形式)
- 面向对象编程三大特性: 封装 继承 多态
- String 和 String StringBuffer 、StringBuilder
- String str="i"与 String str=new String("i")
- String a = new String("a"+"b")
- 装箱和拆箱
- == 和 equals 的区别是什么?
- hashCode() 和 equals()
- Math. round(-1. 5) 等于多少?
- 静态⽅法和实例⽅法
- 成员变量与局部变量
- 抽象类
- Java 中的 IO 流
- 深拷⻉ vs 浅拷⻉
- 如何实现对象的深拷贝
- 序列化
- JAVA创建对象的几种方式
基础
基本数据类型(字节)
byte 1 short 2 int 4 long 8
float 4 double 8 boolean 1 char 2
JDK 和 JRE
- JDK 是 Java Development Kit,它是功能⻬全的 Java SDK。它拥有 JRE 所拥有的⼀切,还有编译器(javac)和⼯具(如 javadoc 和 jdb)。它能够创建和编译程序。
- JRE 是 Java 运⾏时环境。它是运⾏已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的⼀些基础构件。但是,它不能⽤于创建新程序。
static
- 静态变量:静态变量被所有的对象所共享,也就是说我们创建了一个类的多个对象,多个对象共享着一个静态变量,如果我们修改了静态变量的值,那么其他对象的静态变量也会随之修改。
- static修饰的方法称作静态方法,这样我们就可以通过“类名. 方法名”进行调用。由于静态方法在类加载的时候就存在了,所以它不依赖于任何对象的实例就可以进行调用,因此对于静态方法而言,是木有当前对象的概念,即没有this、super关键字的。因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。
- static修饰的代码块也叫静态代码块,会随着JVM加载类的时候而加载这些静态代码块,并且会自动执行。它们可以有多个,可以存在于该类的任何地方。JVM会按照它们的先后顺序依次执行它们,而且每个静态代码块只会被初始化一次,不会进行多次初始化。
- 普通类是不允许声明为静态的,只有内部类才可以,被static修饰的内部类可以直接作为一个普通类来使用,而不需先实例一个外部类。 静态内部类只能访问外部类的静态成员,否则编译会报错。
- 代码块执行顺序静态代码块——> 构造代码块 ——> 构造函数——> 普通代码块
final 关键字
- 修饰的类叫最终类,该类不能被继承。
- final 修饰的方法不能被重写。
- 修饰变量:当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。
finally关键字
finally 作为异常处理的一部分,它只能在 try/catch 语句中,并且附带一个语句块表示这段语句最终一定被执行(无论是否抛出异常),经常被用在需要释放资源的情况下,System.exit (0)退出JVM可以阻断 finally 执行。
public static int fun() {
int i=1; //1
try {
i++; //2
return i; //返回2
}catch(Throwable e){
//不执行
i++;
}finally {
i++; //3,执行后返回try return
}
return i;
}
//返回 2
public static int fun() {
int i=1; //1
try {
i++; //2
return i; //返回2
}catch(Throwable e){
//不执行
i++;
}finally {
i++; //3,执行后return返回,程序结束
return i;
}
}
//返回 3
Java 和 C++的区别
- 都是⾯向对象的语⾔,都⽀持封装、继承和多态
- Java 不提供指针来直接访问内存,程序内存更加安全
- Java 的类是单继承的,C++ ⽀持多重继承;虽然 Java 的类不可以多继承,但是接⼝可以多继承。
- Java 有⾃动内存管理机制,不需要程序员⼿动释放⽆⽤内存
- 在 C 语⾔中,字符串或字符数组最后都会有⼀个额外的字符‘\0’来表示结束。但是,Java 语⾔中没有结束符这⼀概念。
构造器 Constructor 是否可被 override?
Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到⼀个类中有多个构造函数的情况。
a+=b 和 a=a+b 有什么区别吗?
+= 操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型,
a=a+b则不会自动进行类型转换
byte a = 127;
byte b = 127;
b = a + b; // 报编译错误:cannot convert from int to byte b += a;
Iterator 和 增强for
for (Integer i : list) {
System.out.print(i + ",");
}
Integer i; 定义一个临时变量i
Iterator iterator = list.iterator(); 获取List的迭代器
iterator.hasNext(); 判断迭代器中是否有未遍历过的元素
i = (Integer)iterator.next(); 获取第一个未遍历的元素,赋值给临时变量i
System.out.println(i) 输出临时变量i的值
//Iterator 在执行的时候是不允许被迭代的对象被改变的
重载和重写的区别(都是多态的表现形式)
- 重载就是同样的⼀个⽅法能够根据输⼊数据的不同,做出不同的处理
参数相同,返回值不同的重载不被允许。 - 重写就是当⼦类继承⾃⽗类的相同⽅法,输⼊数据⼀样,但要做出有别于⽗类的响应时,你就要覆盖⽗类⽅法。子类重写父类的方法,返回值和抛出异常的范围要小于等于父类。
面向对象编程三大特性: 封装 继承 多态
- 封装把⼀个对象的属性私有化,同时提供⼀些可以被外界访问的属性的⽅法**.**
- 继承使⽤已存在的类的定义作为基础建⽴新类的技术,新类的定义可以增加新的数据或新的功能,也可以⽤⽗类的功能,但不能选择性地继承⽗类。通过使⽤继承我们能够⾮常⽅便地复⽤以前的代码。
- 多态是同一个行为具有多个不同表现形式或形态的能力,就是同一个接口,使用不同的实例而执行不同操作,多态性是对象多种表现形式的体现。可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
String 和 String StringBuffer 、StringBuilder
- String 和 StringBuffer、StringBuilder 的区别:
String 声明的是不可变的对象(使⽤ final 关键字修饰字符数组来保存字符串, private final charvalue[] ),每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。 - StringBuffer 和 StringBuilder 的区别:
StringBuffer 对⽅法加了同步锁或者对调⽤的⽅法加了同步锁,是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
String str="i"与 String str=new String(“i”)
- 使用String a = “aaa” ;,程序运行时会在常量池中查找”aaa”字符串,若没有,会将”aaa”字符串放进常量池,再将其地址赋给a;若有,将找到的”aaa”字符串的地址赋给a。
- 使用String b = new String(“aaa”);`,程序会在堆内存中开辟一片新空间存放新对象,同时会将”aaa”字符串放入常量池,相当于创建了两个对象,无论常量池中有没有”aaa”字符串,程序都会在堆内存中开辟一片新空间存放新对象。
String a = new String(“a”+“b”)
- 根据常量优化机制,String a = new String(“a”+“b”)在编译后会直接等同于String a = new String(“ab”),接着就可以套用你文章中的内容了;先查找"ab"是否已在常量池中,若无则创建字面量+堆上建引用对象
装箱和拆箱
- Java是面向对象的高级语言,号称“一切皆是对象”。但是,Java的八种基本数据类型,并不是对象,为了解决这个问题,Java的设计者们搞出了包装类。
1、创建的方式不同,包装类需要使用new来创建,基本类型不需要。
2、存储的位置不同,包装类是把对象放在堆中,然后在栈中引用这些对象。基本类型直接将值保存在栈中。
3、初始化的值不同,包装类对象的初始值为null,而基本类型不是。
4、参数的传递不同,包装类是对象引用的传递,基本类型是值传递 。 - 自动装箱 Integer i = 10; 实际调用方 法 Integer i = Integer.valueOf(1);
- 自动拆箱 int n = i; 实际调用方法 int n=i.intValue()
在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。简要的说就是在Integer类中有一个静态内部类IntegerCache,在IntegerCache类中有一个Integer数组,用以缓存当数值范围为-128~127时的Integer对象。
== 和 equals 的区别是什么?
对于基本类型和引用类型, == 的作用效果是不同的,如下所示:
- 基本类型:比较的是值是否相 同;
- 引用类型:比较的是引用是否相同;
public boolean equals(Object obj) {
return (this == obj);
}
equals 本质上就是 ==,只不过 String 和 Integer 等类重写了 Object 的equals 方法,把比较引用比较改成了值比较。
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;
}
hashCode() 和 equals()
- 重写 equals() 时必须重写hashCode()?
hashCode()规定两个相同的对象,返回值一定相等;而重写后的equals()方法,比较的是两个对象的值,值相等时便认为他们是同一个对象,此时调用hashCode()方法也需要返回相同的哈希值,因此必须重写hashCode();
- hashcode 值相等,两个对象不⼀定是相等的?
因为 hashCode() 所使⽤的杂凑算法也许刚好会让多个对象传回相同的杂凑值。
Math. round(-1. 5) 等于多少?
等于 -1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃。
静态⽅法和实例⽅法
- 在外部调⽤静态⽅法时,可以使⽤"类名.⽅法名"的⽅式,也可以使⽤"对象名.⽅法名"的⽅
式。⽽实例⽅法只有后⾯这种⽅式。调用实例方法需要创建对象,静态⽅法可以⽆需创建对象。 - 静态⽅法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态⽅法),⽽不
允许访问实例成员变量和实例⽅法;实例⽅法则⽆此限制。
成员变量与局部变量
- 从语法形式上看:成员变量是属于类的,⽽局部变量是在⽅法中定义的变量或是⽅法的参数;成员变量可以被 public , private , static 等修饰符所修饰,⽽局部变量不能被访问控制修饰符及 static 所修饰;
- 从变量在内存中的存储⽅式来看:如果成员变量是使⽤ static 修饰的,那么这个成员变量是属于类的,如果没有使⽤ static 修饰,这个成员变量是属于实例的。对象存于堆内存,如果局部变量类型为基本数据类型,那么存储在栈内存,如果为引⽤数据类型,那存放的是指向堆内存对象的引⽤或者是指向常量池中的地址。
- 从变量在内存中的⽣存时间上看:成员变量是对象的⼀部分,它随着对象的创建⽽存在,⽽局部变量随着⽅法的调⽤⽽⾃动消失。
- 员变量如果没有被赋初值:则会⾃动以类型的默认值⽽赋值(⼀种情况例外:被 final 修饰的成员变量也必须显式地赋值),⽽局部变量则不会⾃动赋值。
抽象类
- 普通类和抽象类有哪些区别?
- 普通类不能包含抽象方法,抽象类可以包含抽象方法。
- 抽象类不能直接实例化,普通类可以直接实例化。(抽象类就是用来继承的 不能实例化)
- 抽象类能使用 final 修饰吗?
不可以,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类,提示错误信息。 - 接口和抽象类有什么区别?
- 实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
- 构造函数:抽象类可以有构造函数;接口没有构造函数。
- 实现数量:类可以实现很多个接口;但是只能继承一个抽象类。
- 访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。(抽象类如果有抽象方法 子类继承后必须重写 抽象方法本身是为了重写的 因此不能用private修饰)
Java 中的 IO 流
- 分类
- 按功能来分:输入流(input)、输出流(output);
- 按类型来分:字节流(InputStream/OutputStream)和字符流(Reader/Writer)。
字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。
- IO 模型
- BIO:Block IO 同步阻塞式 IO,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式!它的特点是模式简单使用方便,并发处理能力低。
- NIO:Non IO 同步非阻塞 IO,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。适用低负载、低并发的应⽤程序。
- AIO:是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。
深拷⻉ vs 浅拷⻉
- 浅拷⻉:对基本数据类型进⾏值传递,对引⽤数据类型进⾏引⽤传递般的拷⻉,此为浅拷⻉。
String a = new String(“1”);
String b = a; - 深拷⻉:对基本数据类型进⾏值传递,对引⽤数据类型,创建⼀个新的对象,并复制其内容,此为深拷⻉。
如何实现对象的深拷贝
- 构造器方法实现:每个对象都new出来,作为构造器的参数传递进去,创建新的对象
- 实现Java Object的Cloneable接口,重写clone方法,果需要执行深拷贝的类中有大量的引用存在,那么需要对每个引用都要实现Cloneable接口并重写clone方法,这看起来是一个十分繁琐的事情,当然其实还有一种方法比较简便就是使用序列化机制来实现深拷贝。
- 实现Serializable接口(该接口仅是一个标志,告诉JVM该类的对象可以被序列化),先将源对象进行序列化,再反序列化生成拷贝对象
序列化
如果希望将我们的对象能以字符串形式存储到Redis等中间件中时,我们可以通过使用不同的序列化协议将对象序列化成文本存储。当需要的时候读取数据再进行反序列化。
- 序列化:序列化就是将jvm内存中对象的状态按照约定的某种方式转换成二进制字节流的过程。
- 反序列化:按照序列化时的约定方式从二进制字节流中重建对象的过程。
- 主要用到文件输入/出流 + 对象输入/流
JAVA创建对象的几种方式
- new 通过构造器创建对象
- 反射 (User)Class.forName(“com.cc.xx.User”).newInstance();
newInstance()方法时实际上是利用默认构造器来创建该类的实例 - 调用对象的clone()方法(要实现Clonable接口 直接复制一个属性相同的对象 不用构造器)
- 序列化和反序列化(实现Serializable接口)
文章评论