static 在 java 中是一个非常重要的关键字,主要用于修饰类、成员变量、成员方法,除此之外,我们还可以将代码块修饰为 static,被 static 修饰的方法或者变量不需要依赖对象的实例化,只要类被加载,被static修饰的方法或者属性就能被使用。
static 修饰成员变量
被static 修饰的成员变量被称为:静态变量。静态变量被所有对象共享,在内存中只有一个副本,在程序加载静态变量所属类的时候,静态变量就会被初始化。非静态变量是对象级别的,也就是说非静态变量的创建永远跟随着对象的实例,存在多个副本。
static 修饰方法
被 static 修饰的方法称为:静态方法。由于静态方法也不需要依赖对象,所以对于静态方法来说,是没有 this 的,并且静态变量不能访问非静态方法,原因很简单,静态方法的加载时间是跟随类的加载,而非静态方法需要对象被实例化之后才能被使用,会导致在静态方法里面根本就找不到你要调用的那么非静态方法。但是非静态方法是可以调用静态方法的,原因也很简单,非静态方法存在的时候,静态方法一定存在,但是静态方法存在的时候非静态方法不一定存在。
static 修饰代码块
被 static 修饰的代码块成为:静态代码块。静态代码块可以存在一个类的任何位置,跟随着类的加载被执行,一个类中可以存在多个静态代码块,他们严格的按照 stattic 的顺序来执行每个 静态代码块,并且每个 静态代码块只会被程序执行一次,由于这个特性,静态代码块用的好的话可以提升程序的性能。
static 误区
与 C/C++不同的是,java 中的static关键字不会影响到变量或者方法的作用域,只有 private、public、protected 可以影响 java 中变量或者方法的作用域。
final关键字
final:最终的,通常表示这是无法修改的。一个属性在什么样的情况下才会被设计成无法修改呢?可能是因为设计和效率。
final修饰的数据:表示数据是永恒不变的,比如:
-
一个永远不改变的编译时常量。
-
一个在运行时被初始化的值,而程序员不希望它被修改。
编译时常量:编译器可以将该常量带入任何可能使用到它的计算中,可以在编译时执行计算,减轻了一些运行时的负担。需要注意的是,java在定义这些常量时必须是基本数据类型,并且以 final 关键字修饰,并且需要给定初始值。
一个既是 static 优势 final 修饰的域只占用一段不饿能该百年的存储空间。
当 final 修饰的不是基本数据类型,而是引用类型时(比如:对象),这个时候容易让人犯迷糊,final 可以让基本数据类型的值永恒不变,final 可以让引用类型的引用永恒不变,但是引用对象的内容可以改变(不能将一个数据的引用地址0x1 变成 0x2,但是可以改变0x1 对象中的属性,我可以将0x1对象的 name 属性改成任意值)。
final 修饰类或方法:
修饰类:表示这个类是永恒不变的,不可以被继承、不可以被覆盖,该类下的所有方法不能被重写,该类下的所有成员变量不能被覆盖。
修饰方法:继承类无法重写被 final 修饰的方法。
private 与final
一个类中,所有的 private 方法或者属性都是被隐式的指定为 final,由于private 的访问权限,所以子类无法重写 private 的方法,我们可以对 private 方法添加 final 关键字,但是这并不能给该方法增加任何额外的意义,语法不会报错,但是这样写显得有点重复。
请注意,final 类禁止继承,所以 final 类下的所有方法都会被隐式的指定为 final ,无法重写他们。 在 final 类中可以给方法添加 final 关键字,但是没有什么意义。
建议
在设计类的时候,尽可能的将类设计成 final ,出于设计考虑,你希望不希望有人来修改这个类的任何东西,但是你无法预测是否有人会修改你的类,所以,如果你希望该类或者方法不被修改,那么最好将他设置为final 。
String常用的方法有哪些
-
length():返回字符串的长度。
-
isEmpty():判断字符串的长度是否等于0( value.length == 0)
-
charAt():返回指定索引下的字符。
-
getBytes():字符串转字节。
-
equals():判断两个字符串的值是否相同
-
equalsIgnoreCase():比较两个字符串是否相等,不区分大小写。
-
compareTo():比较对应字符的大小(ASCII码顺序),如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的长度差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方结束。
-
startsWith():检测字符串是否以指定的前缀开始。
-
endsWith():检测字符串是否以指定的后缀结束。
-
hashCode():返回字符串的hashCode。
-
indexOf():返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
-
lastIndexOf():返回指定字符在此字符串中最后一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
-
substring():截取字符串。
-
concat():字符串拼接。
-
replace():字符串替换。
-
contains():判断当前字符串是否包含某个字符。
-
split():字符串分割,返回一个数组。
-
join():将集合或数组通过指定字符分割组合成字符串。
-
toLowerCase():将所有字符转换为小写。
-
toUpperCase():将所有字符转换为大写。
-
trim():去除字符串前后空格。
-
toCharArray():将此字符串转换为新的字符数组。
-
format():使用指定的格式字符串和参数返回格式化字符串。
-
valueOf():返回参数的字符串表示形式。
-
intern():该方法的作用是把字符串加载到常量池中(jdk1.6常量池位于方法区,jdk1.7以后常量池位于堆)
== 和 equals 的区别是什么
java中数据结构分为基本数据类型和引用类型,他们在使用 == 、equals 时的含义也不相同,我们分别来看看他们在基本数据类型和引用类型中的具体表现吧。
==
-
基本数据类型:比较两个值是否相同。
-
引用类型:比较两个对象是否相同(比较两个对象的引用地址是否相同)。
equal
Object 类中的 equals 方法和 == 的效果是一样的,有些类(String、Integer等)重写了 Object 类中的 equals 方法,将他们用于值的比较。
我们先来看看 Object 类中的 equals 方法源码
public boolean equals(Object obj) {
return (this == obj);
}
我们再来看看 String类中的 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;
}
所以当我们判断两个对象是否相同使用 equals 其实和使用 == 是一样的,因为对象没有重写 equals 方法(当然,我们也可以重写 equals 方法 来实现对比值是对象的属性是否相同来判断对象是否相同),而当我们使用new Stirng() 创建的字符串 通过 equals 方法进行比较时,他们对比的就是字符串的值,如果值相同就返回 true,不会对比内存地址,因为 String 重写 Object类中的 equals 方法。
重载和重写的区别?
重载:发生在同一个类中,方法名必须相同,实质表现就是多个具有不同的参数个数或者类型的同名函数(返回值类型可随意,不能以返回类型作为重载函数的区分标准),返回值类型、访问修饰符可以不同,发生在编译时。
重写: 发生在父子类中,方法名、参数列表必须相同,是父类与子类之间的多态性,实质是对父类的函数进行重新定义。返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。
修饰符public、private、protected,以及不写(默认)时的区别?
private:私有,只有当前类有操作权。
default:当成员变量没有访问修饰符时默认为default,对于同一个包下,它相当于 public(公开),不同包下相当于 private (私有)。
protected:受保护,对同一个包中的类或者子类相当于 public(公开),对于不同包下且没有父子关系的相当于 private(私有)。
public:公开,对所有类都是公开的。
| 修饰符 | 当前类 | 同包 | 子类 | 其他包 |
| :-: | :-: | :-: | :-: | :-: |
| private | ✔ | ✖ | ✖ | ✖ |
| default | ✔ | ✔ | ✖ | ✖ |
| protected | ✔ | ✔ | ✔ | ✖ |
| public | ✔ | ✔ | ✔ | ✔ |
注意:
可以修饰外部类的修饰符只有 public 和 default。
public 修饰外部类时,在同一包内,可以访问,无需导包;同一包外可以访问,需要导包。
default 修饰外部类时,在同一包内,可以访问,无需导包;同一包外,无法访问。
构造器Constructor是否可被override?
构造器Constructor不能被继承,因此不能重写Override,但可以被重载Overload。
Constructor不能被继承,所以Constructor也就不能被override。每一个类必须有自己的构造函数,负责构造自己这部分的构造。子类不会覆盖父类的构造函数,相反必须负责在一开始调用父类的构造函数。
String、StringBuffer、StringBuilder的区别
String:字符串常量,字符串长度不可变。Java 中 String 是 immutable(不可变)的。
StringBuffer:字符串变量(Synchronized,即线程安全),线程安全的可变字符序列,可以改变字符序列的长度和内容,如果需要频繁的对字符串进行拼接,StringBuffer 相对于 “+” 的重载效率会高得多,如果需要将 StringBuffer 转 String,只需要调用 StringBuffer 的toString() 方法。
StringBuilder:字符串变量(非线程安全,JDK1.5 引入)。在内部,StringBuilder 对象被当作是一个包含字符序列的变长数组。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。
三者区别
String 和 StringBuffer区别主要在性能方面,由于 String 是不可变对象,所以通过 “+” 来拼接的字符串都会生成一个新的 String 类,然后将指针指向新的 String 对象。这并不能说明 “+” 的性能就弱于StringBuffer,我们知道,在 JDK 1.5 之后引入了 StringBuilder, “+” 是通过 StringBuilder 实现,对于一般的字符串拼接,“+” 和 StringBuilder 的性能其实没有什么差别,因为 JVM 会对 String 的 “+” 做优化,既然这样的话,是否就没有必要使用 StringBuffer 和 StringBuilder 了呢?不是的,如果你在某个循环中对字符串进行拼接, “+” 会在循环内构造 StringBuilder ,也就是说,循环了多少次,StringBuilder 就被创建了多少次。而 StringBuffer 和 StringBuilder 在循环拼接的时候只需要创建一个对象即可,性能非常的明显。
对于 StringBuffer 和 StringBuilder ,他们都是 AbstractStringBuilder 抽象类的子类,都有着相同的实现,主要区别在于 StringBuffer 线程安全,而 StringBuilder 非线程安全,这也必然会导致 StringBuilder 的性能高于 StringBuffer 。
最后 String、StringBuffer、StringBuilder 的性能由高到低:StringBuilder > StringBuffer > String。
final, finally, finalize的区别
final:用于声明属性,方法和类,分别表示属性不可交变,方法不可覆盖,类不可继承。请参考前文中的final关键字的介绍。
finally:Java 中的 Finally 关键一般与try一起使用,在程序进入try块之后,无论程序是因为异常而终止或其它方式返回终止的,finally块的内容一定会被执行 。
finalize:是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等。
通常来说,这三个关键字之前没有什么必然的联系,只是名字有点像,给人感觉他们很相似。
字节流与字符流的区别
字节流就是普通的二进制流,读出来的是bit。
字符流就是在字节流的基础按照字符编码处理,处理的是char。
什么是java序列化,如何实现java序列化?或者请解释Serializable接口的作用
Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
简单来说:序列化就是将java对象转成字节流的过程,反序列化则是将字节流转成java对象的过程。
java实现序列化
-
原生序列化:java对象实现 Serializable 接口,通过原生流( InputStream/outputStream) 实现。
-
JSON序列化:spring提供了一种默认的json序列化工具包:jackson ,通过ObjectMapper类来实现对象转byte[] 数组或者 json 串转对象的过程。
-
FastJson:阿里巴巴旗下一个员工因为没有好用的序列化工具开发的,虽然目前该工具 bug 满天飞,但是使用 FastJson 的公司还是不少。
-
ProtoBuff序列化: protobuf(Google Protocol Buffers)是Google提供一个具有高效的协议数据交换格式工具库(类似Json),但相比于Json,Protobuf有更高的转化效率,时间效率和空间效率都是JSON的3-5倍。后面将会有简单的demo对于这两种格式的数据转化效率的对比。
abstract class和interface有什么区别?
abstract class:含有 abstract 修饰符的class即为抽象类,abstract 类不能创建实例对象,含有 abstract 修饰的方法的类必须是 abstract 类,但是 abstract 类并不要求其所有方法都是抽象方法。抽象类中定义的抽象方法必须在继承的子类中实现,所以,不能有抽象构造方法或者抽象静态方法。如果子类没有实现父类(抽象类)的所有抽象方法,那么该子类也必须定义为抽象类。
interface:可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。
| | abstract class(抽象类) | interface(接口) |
| — | — | — |
| 类 | 继承关系,只能继承一个,但是可以实现多个接口 | 可以实现多个接口 |
| 数据 | 可以自定义 | 只能是静态的 |
| 方法 | 私有或者受保护的,非抽象方法,但是抽象方法必须实现 | 只能是公开(public)方法 |
| 实例化 | 不能 | 不能 |
| 变量 | 可以定义私有变量、默认,可以在子类中重新定义或者重新赋值 | 只能是公开静态抽象变量(public static abstract ),实现类不能修改 |
| 设计 | is-a | like-a |
| 实现方式 | 继承(extends) | 实现(implements) |
+= 操作符会进行隐式自动类型转换,此处 a+=b 隐式的将加操作的结果类型强制转换为持有结果的类型,而 a=a+b 则不会自动进行类型转换。
从图片中我们可以看出: a+b 的结果应该是 int类型,第一组操作 a += b;之所以没有报错是因为编译器给我自动做了类型转换(将int 类型强转为 short 类型),而 a=a+b 没有做类型转换,所以编译无法通过。
一个类能拥有多个main方法吗?
可以,但只能拥有一个这样的main方法
public static void main(String[] args) {
}
否者程编译将无法通过,警告你的main方法已存在。
java支持什么类型的参数传递?
直接说结论吧,java只有值传递,没有引用传递,解释的话有点长,感兴趣的可以参考一下我之前写的文章:天真,居然还有人认为java的参数传递方式是引用传递。
这里说一下什么是引用传递、什么是值传递
什么是引用传递?
在C++中,函数参数的传递方式有引用传递。所谓引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
什么是值传递?
值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
集合
Arraylist 和 LinkedList的区别?
arrayList
ArrayList 是集合框架的一部分,存在于 java.util 包中。它为我们提供了 Java 中的动态数组。
虽然,它可能比标准数组慢,但在需要对数组进行大量操作的程序中很有帮助。此类位于java.util包中。
特性
-
ArrayList 容器大小可以默认(10)也可以指定,如果数据增多或者减少,ArrayList 容器也会触发扩容或者缩减操作(ArrayList 的扩容通过数据拷贝的方式实现,浅拷贝,所以我们在使用 ArrayList 的时候最好指明容器大小,如果触发扩容次数较多,会严重影响性能)。
-
ArrayList 支持随机访问(下标访问),且效率非常高,时间复杂度:O(1),但是按需查找(查找值),效率和链表一样,时间复杂度为:O(n)。
-
ArrayList 只能被用于包装类型,基本类型不可用。
-
ArrayList 线程不安全的,也就是不同步,可以使用 Vector代替。
ArrayList 工作流程:ArrayList通过构造函数创建了一个容器(默认容器大小 10),调用 add() 方法可以往 ArrayList 中的 elementData 数组中添加数据,
当添加的数据到达10个的时候,就会触发扩容操作,扩容一般是将容器扩大到原来的1.5倍(自己指定扩容的除外,手动扩容会将容器大小变成你指定的大小),
将老的数组拷贝到新数组中,然后废弃老数组。删除数据时,会将数组中指定的元素置空,然后做判断,如果删除的元素不是在最后一个,那么执行一遍数据拷贝,新数组的大小 -1。
LinkedList
链表是一种线性数据结构,其中元素不存储在连续的内存位置。链表中的元素使用指针链接,链表由节点组成,每个节点中至少包含本身数据和指向下一个节点的指针。
单向链表
双向链表
双链表是链表的一种,由节点组成,每个数据结点中都有两个指针,分别指向直接后继和直接前驱。
LinkedList 特点
-
LinkedList是双向链表实现的List(双向链表指的是每个节点都会记录前一个节点和下一个节点的指针)。
-
LinkedList 非线程安全。
-
LinkedList元素允许为null,允许重复元素
-
插入删除效率高,时间复杂度:O(1),查找效率一般,时间复杂度:O(n)。
-
不存在扩容的说法,添加数据直接在尾节点后添加,删除直接将上一个节点的下一个节点指针指向删除节点下一个节点的地址(有点绕。可以仔细品一下)。
ArrayList 和 LinkedList的区别
-
数据结构不同,ArrayList 基于数组;LinkedList 基于链表。
-
效率不同:随机访问 ArrayList 快,增加删除 LinkedList 快。
-
限制不同:ArrayList 由于基于数组,所以需要动态扩容,LinkedList 基于链表,不存在扩容的说法。
-
占用空间不同:ArrayList 需要申请一块连续的内存地址(数组的原因),LinkedList 对内存连续性没有要求(链表的原因)。
HashMap、HashSet、HashTable的区别?
HashMap
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射,数据结构主要是:数组+链表的方式存储,jdk1.8 之后引入了红黑树用来优化链表过长问题。
HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。
HashMap 是无序的,即不会记录插入的顺序。
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
一条记录的键为 null,不支持线程同步。
HashMap 是无序的,即不会记录插入的顺序。
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-jTWkNUHw-1715777172080)]
[外链图片转存中…(img-3dWfnFhD-1715777172081)]
[外链图片转存中…(img-lFlqDpPb-1715777172081)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!