0、Java语言
1.java和c++
2.编译和解释
3.jre和jdk,jvm
简单来说,编译型语言是指编译器针对特定的操作系统将源代码一次性翻译成可被该平台执行的机器码;解释型语言是指解释器对源程序逐行解释成特定平台的机器码并立即执行。
Java 语言既具有编译型语言的特征,也具有解释型语言的特征,因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(*.class 文件),这种字节码必须由 Java 解释器来解释执行。因此,我们可以认为 Java 语言编译与解释并存。
1、基本数据类型和包装类
=,+,-,*,/会自动拆箱装箱
Java 基本类型的包装类的大部分都实现了常量池技术,即 Byte,Short,Integer,Long,Character,Boolean;前面 4 种包装类默认创建了数值[-128,127] 的相应类型的缓存数据,Character 创建了数值在[0,127]范围的缓存数据,Boolean 直接返回 True Or False。如果超出对应范围仍然会去创建新的对象。
2、String, StringBuffer and StringBuilder
1、可变性
-
String 不可变
-
StringBuffer 和 StringBuilder 可变,使用 append 方法在末尾添加新的字符串
2、线程安全
-
String 不可变,是线程安全的
-
StringBuffer:可变长字符串,效率低,线程安全,使用 synchronized 同步
-
StringBuilder:可变长字符串,效率高,线程不安全
new String("abc")创建几个对象?2个或者一个.
String为什么不可变?String
类中使用 final 关键字修饰字符数组来保存字符串,private final char value[]
,所以String
对象是不可变的。而 StringBuilder
与 StringBuffer
都继承自 AbstractStringBuilder
类,在 AbstractStringBuilder
中也是使用字符数组保存字符串char[]value
但是没有用 final
关键字修饰,所以这两种对象都是可变的。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;
......
}
3、static和final
final
1、数据
-
对于基本类型,final 使数值不变;
-
对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。
final int x = 1; //x 值不能变 final A y = new A(); //y 不能改变引用 y.a = 1;
2、方法
声明方法不能被子类重写。
private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。
3、类
被 final 声明的类不能被继承。final不能直接修饰接口,但是可以修饰接口里面的方法和变量
static
1、静态变量
-
静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。
-
实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。
2、静态方法
静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。
静态方法中只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字,因为这两个关键字与具体对象关联。
3、静态语句块
静态语句块在类初始化时运行一次。
4、静态内部类
非静态内部类依赖于外部类的实例,也就是说需要先创建外部类实例,才能用这个实例去创建非静态内部类。而静态内部类不需要。
静态内部类不能访问外部类的非静态的变量和方法。
4、equals和==,hashCode(),clone(),toString(),拷贝
a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
clone() 是 Object 的 protected 方法,若一个类不显式去重写 clone(),那么这个类的实例便不能调用 clone() 方法
5、抽象类和接口
抽象类和抽象方法都使用 abstract 关键字进行声明。如果一个类中包含抽象方法,那么这个类必须声明为抽象类。抽象类中也可以有普通方法和成员变量,也可以没有抽象方法。
抽象类和普通类最大的区别是,抽象类不能被实例化,只能被继承。
抽象类(Abstract Class)是一个被声明为抽象的类,不能被实例化,只能被继承。抽象类可以包含抽象方法和非抽象方法。抽象方法是没有具体实现的方法,只有方法签名,需要子类去实现。非抽象方法则有具体的实现,子类可以直接继承并使用。抽象类可以拥有成员变量和构造方法。通过继承抽象类,子类可以获取父类的属性和方法,并且必须实现父类的抽象方法。抽象类的主要作用是为了提供一种通用的模板或者基类,用于子类的继承和扩展。
接口(Interface)是一种完全抽象的类,它只定义了一组方法的签名,而没有任何具体实现。接口中的方法都是抽象方法。接口不能包含成员变量,但可以包含常量。类可以实现一个或多个接口,并且必须实现接口中定义的所有方法。通过实现接口,类可以定义自己的行为规范,实现多态性。接口的主要作用是定义一组规范,用于不同类的实现,实现类可以根据接口的规范来提供具体的实现。
区别:
-
抽象类可以有构造方法,而接口不能有构造方法。
-
一个类只能继承一个抽象类,但可以实现多个接口。
-
抽象类可以包含非抽象方法,而接口只能包含抽象方法。
-
抽象类的目的是为了代码复用和继承,而接口的目的是定义规范和实现多态性。
-
抽象类可以拥有成员变量,而接口不能拥有成员变量,只能定义常量。
选择使用抽象类还是接口取决于具体的需求和设计目标。如果需要提供一些默认的实现或者共享的代码,可以使用抽象类。如果需要定义一组规范,让不同的类去实现,可以使用接口。在实际开发中,常常会同时使用抽象类和接口来实现灵活的设计。
5、继承
1.重写和重载
-
this.func(this)
-
super.func(this)
-
this.func(super)
-
super.func(super)
方法的重写要遵循“两同两小一大”(以下内容摘录自《疯狂 Java 讲义》,issue#892 ):
-
“两同”即方法名相同、形参列表相同;
-
“两小”指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;
-
“一大”指的是子类方法的访问权限应比父类方法的访问权限更大或相等。
⭐️ 关于 重写的返回值类型 这里需要额外多说明一下,上面的表述不太清晰准确:如果方法的返回类型是void和基本数据类型,则返回值重写时不可修改。但是如果方法的返回值是引用类型,重写时是可以返回该引用类型的子类的。
Java 为什么不能通过返回值判断重载:特征签名
2.抽象类
抽象类不能被实例化,只能被继承。
非抽象的类要实现接口,需要把接口中的方法都加以实现,那如果我只想用其中一个方法而又不想重写其它方法该怎么办呢 ?这个时候我们就需要抽象类了,抽象类可以重写接口中的方法,并添加自己的方法。
-
从使用上来看,一个类可以实现多个接口,但是不能多继承
-
接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。
-
接口的成员只能是 public 的(Java 8 开始也可定义 default 方法,Java 9 之后允许将方法定义为 private),而抽象类的成员可以有多种访问权限。
6.反射,异常,泛型,注解
反射:java.lang.Class 和 java.lang.reflect 一起对反射提供了支持
throw 和 throws区别: throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。 throws 属于异常处理的一种方式,声明在方法的声明处。
try-catch-finally
异常:在 Java 中,所有的异常都有一个共同的祖先 java.lang
包中的 Throwable
类。Throwable
类有两个重要的子类: Error 和 Exception。
Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除。Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。
注解的本质就是一个继承了 Annotation 接口的接口。
7、面向对象
1.面向对象和面向过程的区别
2.成员变量与局部变量,静态方法与实例方法
3.创建一个对象用什么运算符?对象实体与对象引用有何不同?
Object obj = new Object();
简单的一句话,包含了三个动作:
4.一个类的构造方法的作用是什么? 若一个类没有声明构造方法,该程序能正确执行吗? 为什么? 构造方法有哪些特性?
主要作用是完成对类对象的初始化工作。
5.在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
帮助子类做初始化工作。其目的是确保父类中的实例变量和方法在子类中都能够被正确初始化和使用。
存在继承的情况下,初始化顺序为
-
父类(静态变量、静态语句块)
-
子类(静态变量、静态语句块)
-
父类(实例变量、普通语句块)
-
父类(构造函数)
-
子类(实例变量、普通语句块)
-
子类(构造函数)
6.面向对象三大特性
封装、继承、多态
关于继承如下 3 点请记住:
-
子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
-
子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
-
子类可以用自己的方式实现父类的方法。(以后介绍)。
多态,顾名思义,表示一个对象具有多种的状态。具体表现为父类的引用指向子类的实例(例如扒灰)。
8.1 谈谈你对多态性的理解? ① 实现代码的通用性。 ② Object类中定义的public boolean equals(Object obj){ } JDBC:使用java程序操作(获取数据库连接、CRUD)数据库(MySQL、Oracle、DB2、SQL Server) ③ 抽象类、接口的使用肯定体现了多态性。(抽象类、接口不能实例化) 8.2 多态是运行时行为。
总结:编译,看左边;运行,看右边。
8、序列化和transient
关键字
9、获取用键盘输入常用的两种方法
通过 Scanner;通过 BufferedReader
10、BIO,NIO,AIO 有什么区别?
11、BigDecimal
应该使用 new BigDecimal(String)
如123.123,那么如果使用BigDecimal表示,那么他的无标度值为123123,他的标度为3。对于 0.1,使用无标度 1 和标度 1 进行表示。
12、容器
容器主要包括 Collection接口 和 Map接口 两种,Collection 存储着对象的集合,而 Map 存储着键值对映射表。
1.基本概念和主要实现类
2.ArrayList源码分析
jdk8类似懒汉式实现,默认大小10,大于size就扩容,扩容约1.5倍,因为 oldCapacity + (oldCapacity >> 1)
。扩容代价很高,因为调用Arrays.copyOf()
把原数组整个复制到新数组中。删除操作:逐个往前移,时间复杂度为 O(N)。
Fail-Fast:modCount 用来记录 ArrayList 结构发生变化的次数。结构发生变化是指添加或者删除至少一个元素的所有操作,或者是调整内部数组的大小,仅仅只是设置元素的值不算结构发生变化。在进行序列化或者迭代等操作时,需要比较操作前后 modCount 是否改变,如果改变了需要抛出 ConcurrentModificationException。类似乐观锁。
有两个线程(线程A,线程B),其中线程A负责遍历list、线程B修改list。线程A在遍历list过程的某个时候(此时expectedModCount = modCount=N),线程B启动,同时线程B增加一个元素,这是modCount的值发生改变(modCount + 1 = N + 1)。线程A继续遍历执行next方法时,通告checkForComodification方法发现expectedModCount = N ,而modCount = N + 1,两者不等,这时就抛出ConcurrentModificationException 异常,从而产生fail-fast机制。
java.util
包下面的所有的集合类都是fail-fast的,而java.util.concurrent
包下面的所有的类都是fail-safe的。
fail-safe 任何对集合结构的修改都会在一个复制的集合上进行,因此不会抛出ConcurrentModificationException。类似CopyOnWrite机制。
Vector 实现线程安全的方法是在每个方法上使用 synchronized 进行同步。默认长度为 10,扩容2倍。
3.CopyOnWriteArrayList
读写分离原理。缺点:内存占用,数据不一致。
4.HashMap
高八位为什么右移16位? HashMap 的长度为什么是 2 的幂次方?
y%x等价于y&(x-1)
:确定桶下标的最后一步是将 key 的 hash 值对桶个数取模:hash % capacity,如果能保证 capacity 为 2 的 n 次方,那么就可以将这个操作转换为位运算。
jdk7: Entry 类型的数组 table,HashMap 默认大小为 16。Entry 存储着键值对。它包含了四个字段(key, value, next, hash),第0 个桶存放键为 null 的键值对。使用拉链法来解决冲突,头插法,扩容两倍(capacity、size、threshold 和 load_factor),扩容后,需要把原来的键值对重新计算桶下标。
HashTable实现线程安全是通过在每个方法前加 synchronized,因此效率较低
HashMap
可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;HashTable 不允许有 null 键和 null 值,否则会抛出NullPointerException
。
jdk8:尾插法,当数组长度大于 64,链表长度大于等于 8 时会将链表转换为红黑树。
哈希:
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
5.ConcurrentHashMap
jdk7: 分段锁机制,segment,默认的并发级别为 16,也就是说默认创建 16 个 Segment。size 方法先尝试不加锁
jdk8:CAS和sychronized,在 CAS 操作失败时使用内置锁 synchronized。
6.LinkedHashMap
afterNodeAccess()
,实现LRU,头部存放最近最不常用的节点
afterNodeInsertion()
方法在 put 等操作后执行,在 afterNodeInsertion() 方法中,当 removeEldestEntry() 方法返回 true 时(即链表长度大于规定的最大长度)会移除最不常访问的节点,也就是链表首部节点 first。
13、Comparable接口和Comparator接口
14、Arrays.asList
Arrays.asList 得到的集合是不可变的。当使用 Arrays.asList 返回 List 集合时,为 ArrayList,不过注意的是,这里的 ArrayList 和平时使用的 ArrayList 不同,这里的 ArrayList 为 Arrays 的一个私有静态内部类,如下,该类继承自 AbstractList,但是没有对 add、remove 方法进行重写,在 abstract 中,add,remove 方法会直接抛出异常,但是重写了 set 方法,因此可使用 set 方法改变某些索引的值。
15、红黑树
红黑树是自平衡的二叉查找树。
(1)每个结点要么是红的要么是黑的。
(2)根结点是黑的。
(3)每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
(4)如果一个结点是红的,那么它的两个儿子都是黑的。
(5)对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
16、Object obj = new Date()
对象的引用和创建简单的一句话,包含了三个动作:
-
Object obj 在栈内存中创建了一个名为 obj 的 Object 类型引用变量,它可以指向 Object 类型的对象实体;
-
new Object() 在堆内存中创建了一个 Object 类型对象实体;
-
最后用一个“=”将对象引用 obj 指向对象实体 new Object()。
对象引用就是一个变量,有名字,我们可以直接访问,它存储的是一个地址,这个地址指向堆内存中某一个该类型的对象实体;
而对象实体没有名字,我们只能通过对象引用来间接访问它。
最后,只有 new 才会创建对象实体,才会在堆内存中开辟新的空间。