java面试题基础篇 温故而知新 没事在地铁上多看看
答案在路上,自由在风里,风吹哪页读哪页,哪页不懂撕哪页
就是你,你太棒啦~今天也要开开心心呀~
Java语言有哪些特点
Java是一种高级编程语言,具有以下特点:
- 面向对象:Java是一种纯面向对象的语言,所有代码都必须定义在类中。这使得Java非常适合开发大型应用程序和企业级软件。
- 可移植性:Java的跨平台特性使其可以在不同的操作系统上运行,例如Windows、Linux和Mac OS等。
- 简单易学:Java的语法相对简单,易于学习和使用。它还提供了大量的文档和教程,使初学者能够快速入门。
- 高性能:Java的虚拟机(JVM)可以动态地将Java字节码翻译为本地机器代码,从而提高了程序的性能。
- 安全性:Java提供了强大的安全功能,例如沙箱环境和安全管理器,可以帮助保护应用程序免受恶意代码的攻击。
- 大量的类库:Java拥有丰富的类库,涵盖了各种领域,例如网络编程、图形用户界面、数据库访问等。
- 开放源代码:Java是开源的,任何人都可以查看和修改其源代码,这使得Java社区可以不断改进和扩展其功能。
面向对象和面向过程的区别
面向对象(Object-Oriented,简称 OO)和面向过程(Procedural,简称 PO)是两种不同的编程范式。它们的主要区别在于关注点、设计原则和代码组织方式。以下是面向对象和面向过程之间的一些主要区别:
- 关注点:
- 面向对象编程关注数据和行为之间的关系,强调封装、继承和多态性。
- 面向过程编程关注解决问题的方法,强调输入、处理和输出。
- 设计原则:
- 面向对象编程的设计原则包括封装、继承和多态性。封装确保数据和方法在类内部隐藏,外部无法直接访问;继承允许子类从父类继承属性和方法;多态性允许不同类的对象对同一消息作出不同的响应。
- 面向过程编程的设计原则包括模块化、结构化和自顶向下的分解。模块化使得程序更容易理解和维护;结构化编程有助于提高代码的可读性和可维护性;自顶向下的分解将问题分解为更小的、易于管理的部分。
- 代码组织方式:
- 面向对象编程通常使用类和对象来表示实体和关系,通过调用方法实现功能。这种方式使得代码更加模块化、可重用和易于维护。
- 面向过程编程通常使用函数和过程来表示任务,通过调用函数实现功能。这种方式使得代码更加结构化、易于理解和调试。
面向对象编程和面向过程编程的主要区别在于关注点、设计原则和代码组织方式。面向对象编程强调数据和行为之间的关系,以及封装、继承和多态性等设计原则;而面向过程编程关注解决问题的方法,以及模块化、结构化和自顶向下的分解等设计原则。在实际开发中,选择合适的编程范式取决于项目需求、团队技能和其他因素
八种基本数据类型的大小,以及他们的封装类
Java八种基本数据类型的大小如下:
- byte:8位,取值范围为-128~127
- short:16位,取值范围为-32768~32767
- int:32位,取值范围为-2^31~2^31-1
- long:64位,取值范围为-2^63~2^63-1
- float:32位,取值范围为1.4E-45~3.4028235E+38
- double:64位,取值范围为4.9E-324~1.7976931348623157E+308
- char:16位,Unicode编码,一个字符占用两个字节,取值范围为’\u0000’~’\uffff’
Java中对应八种基本数据类型的封装类分别为:
- Byte:java.lang.Byte
- Short:java.lang.Short
- Integer:java.lang.Integer
- Long:java.lang.Long
- Float:java.lang.Float
- Double:java.lang.Double
- Character:java.lang.Character
- String:java.lang.String
// 使用byte类型存储整数
byte b = 10; // b的值为10,占用一个字节(8位)
System.out.println("b的值为" + b); // 输出结果为b的值为10
// 使用short类型存储整数
short s = 10; // s的值为10,占用两个字节(16位)
System.out.println("s的值为" + s); // 输出结果为s的值为10
// 使用int类型存储整数
int i = 10; // i的值为10,占用四个字节(32位)
System.out.println("i的值为" + i); // 输出结果为i的值为10
// 使用long类型存储整数
long l = 10L; // l的值为10,占用八个字节(64位)
System.out.println("l的值为" + l); // 输出结果为l的值为10
// 使用float类型存储浮点数
float f = 10F; // f的值为10,占用四个字节(32位),其中小数部分占用了三个字节(24位)
System.out.println("f的值为" + f); // 输出结果为f的值为10.000000
// 使用double类型存储浮点数
double d = 10D; // d的值为10,占用八个字节(64位),其中小数部分占用了七个字节(56位)
System.out.println("d的值为" + d); // 输出结果为d的值为10.0000000000000000000000000000000000000000000000000
标识符的命名规则
Java标识符的命名规则如下:
- 标识符由字母、数字和下划线组成,但是必须以字母或下划线开头。
- Java中的关键字不能作为标识符的一部分,包括public、private、protected、static、interface、abstract、final、volatile、transient等关键字。
- 标识符只能在类、接口、方法和变量中使用。
- Java中的保留字(如if、else、for、while等)也不能作为标识符的一部分。
public class MyClass {
private int myInt; // 整型变量myInt的命名符合Java标识符的命名规则
private String myString = "Hello World"; // 字符串变量myString的命名符合Java标识符的命名规则
public void myMethod() {
double myDouble = 3.14; // 双精度浮点型变量myDouble的命名符合Java标识符的命名规则
}
}
instanceof 关键字的作用
instanceof
是 Java 中的一种关键字,用于检查一个对象是否是某个特定类(或其子类)的实例。它通常与 new
操作符一起使用,以创建一个新对象并检查其类型。
public class Animal {
public void makeSound() {
System.out.println("Animal is making a sound.");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog is barking.");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // 创建一个狗对象
if (animal instanceof Dog) { // 检查动物对象是否是狗的实例
System.out.println("The animal is a dog.");
} else if (animal instanceof Animal) { // 检查动物对象是否是任何动物的实例(包括狗和猫等)
System.out.println("The animal is an animal.");
} else { // 如果不是狗或任何动物的实例,则输出错误信息
System.out.println("The animal is neither a dog nor an animal.");
}
}
}
Java自动装箱与拆箱
Java自动装箱与拆箱是Java语言中的一个重要特性,它允许程序员在不需要显式声明类型的情况下将基本数据类型转换为对象类型。以下是Java自动装箱与拆箱的示例代码:
自动装箱
int a = 5;
Integer b = a; // 将int类型的a自动装箱成Integer类型
System.out.println(b); // 输出结果为5
自动拆箱
Integer c = new Integer(5);
int d = c; // 将Integer类型c自动拆箱成int类型
System.out.println(d); // 输出结果为5
抛出异常
int e = 0;
try {
int f = (int) "0"; // 将字符串"0"强制转换为int类型,会抛出NumberFormatException异常
} catch (NumberFormatException ex) {
System.out.println(ex.getMessage()); // 输出结果为"Value is not a number"
}
最大值和最小值检查
int g = Integer.MAX_VALUE;
if (g == 10) {
System.out.println("g is equal to 10"); // 输出结果为"g is equal to 10"
} else {
System.out.println("g is not equal to 10"); // 输出结果为"g is not equal to 10"
重载和重写的区别
Java中的方法重载(Overloading)和方法重写(Overriding)是面向对象编程中的两个重要概念,它们在功能上有所区别。
- 重载(Overloading): 重载是指在一个类中,方法名相同但参数列表不同的多个方法。编译器根据传递给方法的参数类型和数量来决定调用哪个方法。例如:
public class MyClass {
public void method(int a, int b) {
System.out.println("Method with two integers: " + a + b);
}
public void method(String str) {
System.out.println("Method with a string: " + str);
}
}
在这个例子中,我们定义了两个名为method
的方法,它们具有相同的名称,但参数列表不同。当我们使用这两个方法时,编译器会根据传递给它的参数类型和数量自动选择合适的方法。
- 重写(Overriding): 重写是指子类重新定义与父类同名、同参数列表和返回类型的方法。当子类重写一个父类的方法时,子类的方法将覆盖父类的方法。
class Animal {
public void eat() {
System.out.println("Animal is eating");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("Dog is eating");
}
}
在这个例子中,我们定义了一个名为eat
的方法,它是一个抽象方法,表示动物吃东西的行为。然后我们创建了一个名为Dog
的子类,并重写了eat
方法。当我们创建一个Dog
对象并调用eat
方法时,输出结果将是"Dog is eating",而不是"Animal is eating"。这是因为子类重写了父类的方法
equals与==的区别
Java中的equals()和==都是用于比较两个对象是否相等的方法,但它们之间有一些细微的差别。
- equals()方法是Object类中的方法,而==运算符是基本类型(如int、float等)的方法。因此,如果要比较一个自定义对象与另一个自定义对象,应该使用equals()方法而不是==运算符。
- equals()方法比较的是两个对象的内容是否相等,包括数据类型和值。而==运算符比较的是两个对象的引用是否相等,即它们是否指向同一个内存地址。
public class Person {
private String name;
private int age;
// 构造函数
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
// toString方法
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
在上面的代码中,我们定义了一个Person类,它有两个属性:name和age。我们在类中重写了equals()方法和toString()方法。在equals()方法中,我们首先判断两个对象是否为同一个对象,如果是则返回true,否则通过getClass()方法获取两个对象的类类型并进行比较,最后再比较两个对象的属性值是否相等。在toString()方法中,我们只是简单地将属性值拼接成字符串输出。
Hashcode的作用
Hashcode是Java中用于比较两个对象是否相等的方法。它的作用是返回一个整数,表示当前对象的哈希码值。如果两个对象的哈希码值相同,则说明它们在内存中的地址相同,即相等。
public class HashCodeExample {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "World";
int hash1 = str1.hashCode(); // 计算str1的哈希码值
int hash2 = str2.hashCode(); // 计算str2的哈希码值
System.out.println("str1的哈希码值为:" + hash1);
System.out.println("str2的哈希码值为:" + hash2);
if (hash1 == hash2) { // 判断两个字符串的哈希码值是否相等
System.out.println("str1和str2相等");
} else {
System.out.println("str1和str2不相等");
}
}
}
str1的哈希码值为:504839640
str2的哈希码值为:720774163
str1和str2不相等
String、String StringBuffffer 和 StringBuilder 的区别是什
么?
Java中的String
、StringBuffer
和StringBuilder
都是用于处理字符串的类,但它们之间存在一些关键区别。以下是关于这三个类的详细说明:
String
:String
类是Java中最常用的字符串类,它表示一个不可变(immutable)的字符序列。当你创建一个String
对象时,Java会在堆内存中分配一块连续的空间来存储这个字符串。这意味着一旦你创建了一个String
对象,你就无法更改其内容。
String str1 = "Hello, world!";
System.out.println(str1); // 输出: Hello, world!
StringBuffer
:StringBuffer
类是一个可变的字符串缓冲区,允许你在运行时修改字符串。当你创建一个StringBuffer
对象时,Java会分配一块动态增长的内存空间来存储这个字符串。这意味着你可以在程序运行过程中随时修改字符串的内容。
StringBuffer strBuffer = new StringBuffer("Hello, Java!");
strBuffer.append(", World!"); // 修改字符串内容
System.out.println(strBuffer); // 输出: Hello, Java!, World!
StringBuilder
:StringBuilder
类也是一个可变的字符串缓冲区,但它的行为与StringBuffer
类似,但性能更好。当你创建一个StringBuilder
对象时,Java同样会分配一块动态增长的内存空间来存储这个字符串。然而,由于它的内部实现方式不同,它在修改字符串时的速度通常比StringBuffer
快。
StringBuilder stringBuilder = new StringBuilder("Hello, Java!");
stringBuilder.append(", World!"); // 修改字符串内容
System.out.println(stringBuilder); // 输出: Hello, Java!, World!
如果你需要在程序运行过程中频繁地修改字符串,那么使用StringBuilder
或StringBuffer
可能更合适。而如果你只需要创建一个不可变的字符串常量,那么使用String
就足够了。
ArrayList和linkedList的区别
ArrayList和LinkedList都是Java中常用的集合类,它们的主要区别在于内部实现和性能。
- 内部实现:ArrayList是基于数组(Array)实现的,而LinkedList是基于链表(Linked List)实现的。因此,对于对元素的随机访问,ArrayList比LinkedList更快;而对于插入、删除操作,LinkedList比ArrayList更高效。
- 性能:由于ArrayList是基于数组实现的,它在随机访问方面表现非常优秀,但是在插入、删除元素时需要移动后面的元素,所以时间复杂度为O(n),其中n为元素数量。而LinkedList是基于链表实现的,插入、删除元素的时间复杂度为O(1),但是随机访问元素需要遍历整个链表,所以时间复杂度为O(n)。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;
public class ArrayListExample {
public static void main(String[] args) {
// 创建ArrayList对象
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
list1.add(4);
list1.add(5);
System.out.println("ArrayList: " + list1);
// 创建LinkedList对象
LinkedList<Integer> list2 = new LinkedList<>();
list2.add(1);
list2.add(2);
list2.add(3);
list2.add(4);
list2.add(5);
System.out.println("LinkedList: " + list2);
// 添加元素到ArrayList中
Random random = new Random();
for (int i = 0; i < 10; i++) {
int num = random.nextInt(10);
if (list1.contains(num)) {
continue;
} else {
list1.add(num);
}
}
System.out.println("ArrayList: " + list1);
// 添加元素到LinkedList中
for (int i = 0; i < 10; i++) {
int num = random.nextInt(10);
if (list2.contains(num)) {
continue;
} else {
list2.add(num);
}
}
System.out.println("LinkedList: " + list2);
}
}
HashMap和HashTable的区别
Java中的HashMap和HashTable都是用于存储键值对的数据结构,但它们之间有一些重要的区别。
- 线程安全性:HashTable是线程安全的,而HashMap是非线程安全的。这意味着在多线程环境下使用HashMap时需要进行同步处理,否则可能会出现数据不一致的情况。
- 性能:HashMap通常比HashTable更快,因为HashMap内部使用哈希表来实现,而HashTable则是基于数组实现的。
- Null值:HashMap允许Key和Value为null,而HashTable不允许。
- 初始容量和增长因子:HashMap有固定的初始容量和增长因子,而HashTable没有。HashMap的初始容量为16,增长因子为0.75;而HashTable的初始容量为11,增长因子为0.75。
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class MapExample {
public static void main(String[] args) {
// 创建HashMap对象
HashMap<Integer, String> map1 = new HashMap<>();
map1.put(1, "one");
map1.put(2, "two");
map1.put(3, "three");
System.out.println("HashMap: " + map1);
// 创建HashTable对象
HashSet<Integer> set1 = new HashSet<>();
set1.add(1);
set1.add(2);
set1.add(3);
System.out.println("HashTable: " + set1);
}
}
Collection包结构,与Collections的区别
Java Collection包是Java集合框架的核心,它提供了一组接口和类来处理集合。Java Collection包结构包括以下几个部分:
- 接口
Java Collection包中定义了许多接口,例如List、Set、Map等。这些接口定义了集合的基本操作,例如添加元素、删除元素、查找元素等。
- 类
Java Collection包中定义了许多类,用于实现各种接口。例如,ArrayList类实现了List接口,LinkedList类实现了List接口并保持元素的插入顺序,HashMap类实现了Map接口,TreeMap类实现了Map接口并按照自然排序或自定义排序方式对键进行排序等。
- 静态方法
Java Collection包中还定义了一些静态方法,用于创建新的集合对象或对现有集合进行操作。例如,Collections.singletonList()方法可以创建一个只包含一个元素的List集合,Collections.emptyList()方法可以创建一个空的List集合,Collections.unmodifiableList(List list)方法可以将一个List集合转换为不可修改的集合等。
import java.util.ArrayList;
import java.util.List;
public class Example {
public static void main(String[] args) {
// 创建一个List集合对象
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("orange");
// 输出List集合中的元素数量
System.out.println("List集合中的元素数量:" + list.size());
// 将List集合转换为字符串并输出
System.out.println("List集合转换为字符串:" + list);
}
}
java 的四种引用,强软弱虚
Java中四种引用是:
- Strong Reference(强引用):是指在程序中直接使用一个对象时所使用的引用,如果该对象没有其他强引用指向它,那么它会被垃圾回收器回收。
- Soft Reference(软引用):是指在程序中使用一个弱引用来引用一个对象,只有在内存不足时才会被回收。
- Weak Reference(弱引用):是指在程序中使用一个虚引用来引用一个对象,只要任何地方有一个强引用指向它,它就不会被回收。
- Final Reference(最终引用):是指在程序中使用一个永久引用来引用一个对象,无论何时何地,只要存在这个引用,对象就不会被回收。
// 创建一个对象并使用Strong Reference引用它
Object obj = new Object();
ReferenceType objRef = new ReferenceType(obj);
System.out.println("obj的值为:" + obj.toString());
System.out.println("objRef的值为:" + objRef.get());
// 将objRef设置为null,表示不再使用Strong Reference引用它
objRef = null;
System.gc(); // 执行一次垃圾回收
System.out.println("obj的值为:" + obj.toString());
泛型常用特点
Java泛型是Java编程语言的一个重要特性,它允许程序员在编译时为数据类型指定通用类型参数。以下是Java泛型的一些常用特点:
- 类型安全:泛型可以确保在运行时不会发生类型不匹配的问题,从而提高程序的稳定性和安全性。
- 代码重用:通过使用泛型,程序员可以将相同的代码用于不同类型的数据结构,从而提高代码的复用性。
- 类型擦除:Java泛型实现了类型擦除,这意味着在运行时,实际使用的是原始类型,而不是泛型类型。这有助于减少内存开销,提高性能。
public class GenericClass<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
public class Main {
public static void main(String[] args) {
GenericClass<Integer> integerClass = new GenericClass<>();
integerClass.setValue(10);
System.out.println("Value of integerClass: " + integerClass.getValue()); // Output: Value of integerClass: 10
GenericClass<String> stringClass = new GenericClass<>();
stringClass.setValue("Hello, World!");
System.out.println("Value of stringClass: " + stringClass.getValue()); // Output: Value of stringClass: Hello, World!
}
}
我们定义了一个名为GenericClass
的类,它具有一个泛型类型参数T
。然后我们创建了两个不同的GenericClass
实例:integerClass
和stringClass
,它们分别使用了整数和字符串作为泛型类型参数
Java创建对象有三种方式:
- 使用new关键字创建对象
java复制代码Person person = new Person("张三", 20);
- 使用反射机制创建对象
java复制代码Class<?> clazz = Class.forName("com.example.Person");
Object obj = clazz.newInstance();
- 使用工厂模式创建对象
java复制代码// 定义一个工厂类,用于创建Person对象
public class PersonFactory {
public static Person createPerson(String name, int age) {
Person person = new Person(name, age);
return person;
}
}
// 在其他地方调用工厂类创建Person对象
Person person = PersonFactory.createPerson("张三", 20);
有没有可能两个不相等的对象有相同的hashcode
在Java中,如果两个对象的hashCode()方法没有正确实现,那么它们可能会有相同的hashCode值。这是因为hashCode()方法的目的是生成一个整数,用于标识对象在哈希表中的位置,而哈希表使用的是散列算法(如MD5或SHA-1),这些算法并不要求不同的对象具有不同的哈希码值。
java复制代码
public class UnequalHashCodeExample {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "World";
int hashCode1 = str1.hashCode();
int hashCode2 = str2.hashCode();
System.out.println("str1 hash code: " + hashCode1);
System.out.println("str2 hash code: " + hashCode2); if (hashCode1 == hashCode2) {
System.out.println("The two strings have the same hash code!");
} else {
System.out.println("The two strings do not have the same hash code!");
}
}
}
在这个例子中,我们创建了两个字符串对象:str1
和str2
,它们的内容分别为"Hello"和"World"。然后,我们分别调用它们的hashCode()
方法并打印结果。由于这两个字符串的内容不同,我们期望它们的哈希码也不同。然而,当我们运行这段代码时,我们会发现它们确实具有相同的哈希码值(输出结果为:“The two strings have the same hash code!”),这说明在某些情况下,两个不相等的对象确实可能具有相同的哈希码。
深拷贝和浅拷贝的区别是什么?
Java中的深拷贝和浅拷贝都是对象复制的方式,它们的区别在于是否对原始对象及其引用类型进行递归复制。
- 浅拷贝(Shallow Copy): 浅拷贝只复制对象本身及其基本类型属性(如int、float等),而不复制引用类型属性(如String、List等)所指向的对象。换句话说,浅拷贝会创建一个新的对象,但是新对象的引用类型属性仍然指向原始对象。这意味着在原始对象中修改引用类型属性时,新对象的引用类型属性也会受到影响。
public class ShallowCopyExample {
public static void main(String[] args) {
List<Integer> originalList = new ArrayList<>(Arrays.asList(1, 2, 3));
List<Integer> shallowCopyList = new ArrayList<>(originalList); // 浅拷贝
originalList.add(4); // 在原始列表中添加元素4
System.out.println("Original list: " + originalList); // [1, 2, 3, 4]
System.out.println("Shallow copy list: " + shallowCopyList); // [1, 2, 3, 4]
}
}
- 深拷贝(Deep Copy): 深拷贝会递归地复制原始对象及其引用类型属性所指向的对象。这意味着在原始对象中修改引用类型属性时,新对象的引用类型属性不会受到影响。
import java.util.ArrayList;
import java.util.List;
public class DeepCopyExample {
public static void main(String[] args) {
List<Integer> originalList = new ArrayList<>(Arrays.asList(1, 2, 3));
List<Integer> deepCopyList = new ArrayList<>(originalList); // 深拷贝
originalList.add(4); // 在原始列表中添加元素4
System.out.println("Original list: " + originalList); // [1, 2, 3, 4]
System.out.println("Deep copy list: " + deepCopyList); // [1, 2, 3, 4]
}
}
- 点赞
- 收藏
- 关注作者
评论(0)