java基础一

Java基础

文章目录

  • 前言
  • java基本类型和包装类型的区别?
  • 包装类型的缓存机制了解么?
  • 构造⽅法有哪些特点?是否可被 override?
  • 面向对象编程具有三大特征
  • 接⼝和抽象类有什么共同点和区别?
  • 深拷⻉和浅拷⻉区别了解吗?什么是引⽤拷⻉?
  • == 和 equals() 的区别
  • hashCode() 有什么⽤?
  • final 在 Java 中有什么作用?
  • Math.round()方法特性


前言

基础不牢,地动山摇。要成为一名优秀的Java开发工程师,必须对Java基础掌握的非常牢固。学习在于日积月累,坚持不懈,以至熟能生巧尔。


java基本类型和包装类型的区别?

基本类型的特点是它们是直接存储在内存中的,占用固定的内存空间,并且没有对应的方法或属性。

包装类型是为了方便在基本类型和对象之间进行转换而引入的。每个基本类型都有对应的包装类型,包装类型的命名与对应的基本类型相似,但首字母大写。例如:

Byte:对应 byte
Short:对应 short
Integer:对应 int
Long:对应 long
Float:对应 float
Double:对应 double
Character:对应 char
Boolean:对应 boolean
包装类型是类,因此它们具有对应的方法和属性,可以进行更多的操作,比如进行数学计算、转换为字符串等。包装类型还提供了常量和静态方法来操作基本类型的数据。

另外,包装类型还具有自动装箱(Autoboxing)和自动拆箱(Unboxing)的功能。自动装箱是指将基本类型自动转换为对应的包装类型,而自动拆箱是指将包装类型自动转换为对应的基本类型。

总结起来,基本类型是简单的数据类型,直接存储在内存中,而包装类型是对基本类型的封装,提供了更多的功能和操作。在需要使用对象的场景下,可以使用包装类型,而在需要更高效的存储和计算时,可以使用基本类型。


包装类型的缓存机制了解么?

为了提高性能和节省内存,Java在某些情况下会对包装类型的对象进行缓存。具体来说,对于某些范围内的整数和一些常用的浮点数,Java会缓存对应的包装类型对象,以便重复使用,而不是每次都创建新的对象。

缓存机制适用于以下的情况:

整数缓存:对于byte和short范围内的整数(-128到127之间),以及Integer类中预先缓存的整数(-128到127之间),会被缓存为对象,以便重复使用。

Integer a = 10;
Integer b = 10;
System.out.println(a == b); // 输出 true

常用浮点数缓存:对于Float和Double类中预先缓存的一些常用浮点数(如0.0、1.0等),会被缓存为对象,以便重复使用。

Float a = 1.0f;
Float b = 1.0f;
System.out.println(a == b); // 输出 true

需要注意的是,缓存机制只适用于自动装箱的情况,即通过赋值操作或方法调用将基本类型转换为包装类型。如果使用显式的构造函数创建包装类型对象,那么不会使用缓存,而是每次都创建新的对象。

Integer a = new Integer(10);
Integer b = new Integer(10);
System.out.println(a == b); // 输出 false

因此,在比较包装类型对象时,应该使用.equals()方法而不是==运算符,以确保比较的是对象的值而不是引用。

Integer a = 10;
Integer b = 10;
System.out.println(a.equals(b)); // 输出 true

需要注意的是,缓存机制是Java虚拟机的实现相关的,不同的虚拟机实现可能会有不同的缓存策略。因此,在编写代码时,不应该依赖于包装类型的缓存机制,而应该始终使用.equals()方法来比较包装类型对象的值。


构造⽅法有哪些特点?是否可被 override?

构造方法(Constructor)是用于创建对象并初始化对象的特殊方法。构造方法具有以下几个特点:

  1. 方法名与类名相同:构造方法的方法名必须与类名完全相同,包括大小写。

  2. 没有返回类型:构造方法没有返回类型,包括 void。它们的主要目的是初始化对象,而不是返回值。

  3. 可以有参数:构造方法可以接受参数,用于在创建对象时传递初始值。

  4. 可以重载:同一个类可以定义多个构造方法,只要它们的参数列表不同即可。这样可以根据不同的参数组合来创建对象。

  5. 默认构造方法:如果在类中没有显式定义任何构造方法,Java会自动提供一个无参的默认构造方法。默认构造方法不接受任何参数,执行的操作通常是对成员变量进行默认初始化。

构造方法在对象创建时被调用,用于初始化对象的状态。它们通常用于执行以下操作:

  1. 初始化成员变量:构造方法可以在对象创建时对成员变量进行初始化,为对象提供初始值。

  2. 执行必要的操作:构造方法可以执行一些必要的操作,如打开文件、建立数据库连接等。

构造方法可以被继承,但不能被直接重写(override)。子类可以定义与父类相同的构造方法,但并不是重写父类的构造方法,而是在子类中提供了一个独立的构造方法。在子类的构造方法中,可以通过使用 super() 关键字调用父类的构造方法来完成父类的初始化。

需要注意的是,如果父类中没有无参的构造方法,而子类又没有显式地调用父类的构造方法,那么编译器会报错。在这种情况下,子类需要显式地调用父类的构造方法,通过使用 super() 关键字来指定调用父类的特定构造方法。

总结起来,构造方法是用于创建对象并初始化对象的特殊方法,具有与类名相同、没有返回类型、可以有参数、可以重载的特点。它们在对象创建时被调用,用于执行对象的初始化操作。构造方法可以被继承,但不能被直接重写。

当父类中没有无参的构造方法,而子类又没有显式地调用父类的构造方法时,编译器会报错。这是因为在创建子类对象时,会隐式地调用父类的无参构造方法来完成父类的初始化操作。如果父类中没有无参构造方法可供调用,编译器无法确定如何正确地初始化父类,因此会产生编译错误。

是的,当在父类中定义了有参数的构造方法后,如果没有显式地定义无参构造方法,那么父类就不再具有默认的无参构造方法。下面是一个示例来说明这种情况:

class Parent {
    public Parent(int value) {
        // 父类构造方法接受一个int类型的参数
        // 其他代码...
    }
}

class Child extends Parent {
    public Child() {
        // 子类的构造方法没有显式地调用父类的构造方法
        // 其他代码...
    }
}

在上面的示例中,父类 Parent 中定义了一个带有一个 int 类型参数的构造方法,但没有无参构造方法。子类 Child 的构造方法没有显式地调用父类的构造方法。

当我们尝试创建 Child 类的对象时,编译器会报错,因为编译器无法找到合适的父类构造方法来初始化父类。此时,我们需要在子类的构造方法中显式地调用父类的构造方法,通过使用 super() 关键字来指定调用父类的特定构造方法。

修正后的示例代码如下:

class Parent {
    public Parent(int value) {
        // 父类构造方法接受一个int类型的参数
        // 其他代码...
    }
}

class Child extends Parent {
    public Child() {
        super(10); // 显式地调用父类的构造方法
        // 其他代码...
    }
}

在修正后的代码中,子类 Child 的构造方法使用 super(10) 调用了父类 Parent 的构造方法,传递了一个 int 类型的参数。这样就能够正确地初始化父类,并且编译器不会报错。


面向对象编程具有三大特征

  1. 封装(Encapsulation):封装是将数据和操作数据的方法封装在一起,形成一个独立的、可复用的类。通过封装,我们可以隐藏类的内部实现细节,只暴露必要的接口给外部使用。这样可以提高代码的安全性和可维护性,同时也方便了代码的复用和扩展。

  2. 继承(Inheritance):继承是通过定义一个新的类来继承已有类的属性和方法。继承可以建立类之间的层次关系,使得子类可以继承父类的特性,并且可以在此基础上进行扩展或修改。通过继承,我们可以实现代码的重用,减少重复编写类似的代码,同时也可以实现多态。

  3. 多态(Polymorphism):多态是指同一类型的对象在不同的情况下表现出不同的行为。多态可以通过继承和接口实现。在多态中,父类的引用可以指向子类的对象,通过父类的引用调用方法时,根据具体的对象类型,会执行对应子类的方法。多态提高了代码的灵活性和可扩展性,使得程序更易于扩展和维护。

这三大特征共同构成了面向对象编程的基础,通过封装、继承和多态,我们可以更好地组织和管理代码,提高代码的可读性、可维护性和可扩展性。


接⼝和抽象类有什么共同点和区别?

共同点:

  1. 都可以包含抽象方法:接口中的方法都是抽象的,而抽象类可以包含抽象方法和非抽象方法。
  2. 都不能直接实例化:既不能创建接口的实例,也不能创建抽象类的实例。
  3. 都可以被实现或继承:类可以实现接口,子类可以继承抽象类。
  4. 都可以有默认实现的⽅法(Java 8 可以⽤ default 关键字在接⼝中定义默认⽅法)。

区别:

  1. 实现方式:一个类可以实现多个接口,但只能继承一个抽象类。接口使用 implements 关键字实现,抽象类使用 extends 关键字继承。
  2. 成员类型:接口中只能包含常量、抽象方法和默认方法,不能包含普通变量和具体实现的方法。抽象类可以包含普通变量、常量、抽象方法和具体实现的方法。
  3. 构造方法:接口不能有构造方法,因为接口不能被实例化。抽象类可以有构造方法,用于子类的实例化过程。
  4. 多继承限制:一个类可以实现多个接口,但一个类只能继承一个抽象类。这是因为Java中不支持多继承,但允许实现多个接口。
  5. 设计目的:接口用于定义一组相关的操作,强调类的行为。抽象类用于作为子类的模板,提供共享的属性和方法,并可以包含一些默认的实现。
  6. 接⼝中的成员变量只能是 public static final 类型的,不能被修改且必须有初始值,⽽抽象类的成员变量默认 default,可在⼦类中被重新定义,也可被重新赋值。

总的来说,接口更加抽象和规范,用于定义类的行为和能力,强调多态和约束;抽象类更加具体,用于作为子类的基类,提供共享的属性和方法,并可以包含一些默认的实现。选择使用接口还是抽象类,取决于具体的需求和设计目标。


深拷⻉和浅拷⻉区别了解吗?什么是引⽤拷⻉?

浅拷⻉:浅拷⻉会在堆上创建⼀个新的对象(区别于引⽤拷⻉的⼀点),不过,如果原对象内部的属性是引⽤类型的话,浅拷⻉会直接复制内部对象的引⽤地址,也就是说拷⻉对象和原对象共⽤同⼀个内部对象。

深拷⻉ :深拷⻉会完全复制整个对象,包括这个对象所包含的内部对象。

那什么是引⽤拷⻉呢? 简单来说,引⽤拷⻉就是两个不同的引⽤指向同⼀个对象。


== 和 equals() 的区别

对于基本数据类型来说,“ == ”⽐的是值。

对于引⽤数据类型来说,” == “⽐的是对象的内存地址。

equals() 不能⽤于判断基本数据类型的变量,只能⽤来判断两个对象是否相等。 equals() ⽅法存在于 Object 类中,⽽ Object 类是所有类的直接或间接⽗类,因此所有的类都有 equals() ⽅法。
“ = = ” 运算符用于比较两个对象的引用是否指向同一个内存地址。它比较的是对象的引用值,即比较两个对象是否是同一个对象。当使用“ == ”比较两个对象时,它会检查它们的引用是否相等,如果引用指向同一个内存地址,则返回true,否则返回false。

equals()方法是Object类中定义的方法,用于比较两个对象的内容是否相等。默认情况下,equals()方法与” == “”运算符的行为相同,即比较两个对象的引用是否相等。但是,可以通过在类中重写equals()方法来改变其行为,使其比较对象的内容而不是引用。

重写equals()方法时,通常需要满足以下几个条件:

  1. 自反性:对于任意非空对象x,x.equals(x)应该返回true。
  2. 对称性:对于任意非空对象x和y,如果x.equals(y)返回true,则y.equals(x)也应该返回true。
  3. 传递性:对于任意非空对象x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)也应该返回true。
  4. 一致性:对于任意非空对象x和y,如果对象没有发生变化,多次调用x.equals(y)应该始终返回相同的结果。
  5. 非空性:对于任意非空对象x,x.equals(null)应该返回false。

总结:
“” == “比较的是对象的引用是否相等。
equals()比较的是对象的内容是否相等,可以根据需要重写该方法来改变比较的行为。
在使用equals()方法进行对象比较时,需要注意满足重写equals()方法的条件。

hashCode() 有什么⽤?

hashCode()是Java中的一个方法,定义在Object类中,用于返回对象的哈希码(hash code)。哈希码是一个整数值,用于快速确定对象在哈希表等数据结构中的存储位置。

hashCode()方法的主要作用如下:

  1. 在哈希表中的使用:哈希表是一种常用的数据结构,如HashMapHashSet等,它们使用哈希码来确定对象在表中的存储位置。通过计算对象的哈希码,可以快速定位对象在哈希表中的存储位置,提高查找和插入的效率。

  2. 作为对象的标识符:哈希码在某些情况下可以作为对象的唯一标识符。如果两个对象的哈希码相同,不一定表示它们相等,但如果两个对象不相等,则它们的哈希码一定不同。因此,哈希码可以用于快速判断对象是否相等,从而提高对象比较的效率。

  3. 在集合中的使用:在使用集合类(如HashSetHashMap等)存储对象时,为了保证集合中的元素不重复,会使用hashCode()方法来判断两个对象是否相等。当向集合中插入元素或者从集合中查找元素时,会先比较对象的哈希码,如果哈希码相等再调用equals()方法进行进一步的比较。

需要注意的是,为了保证对象的一致性,当重写equals()方法时,通常也需要同时重写hashCode()方法,以满足以下规则:

  • 如果两个对象相等(根据equals()方法的定义),那么它们的哈希码必须相等。
  • 如果两个对象的哈希码相等,不一定表示它们相等(根据equals()方法的定义)。

总结:
hashCode()方法用于返回对象的哈希码,主要用于在哈希表中确定对象的存储位置和作为对象的标识符。在使用集合类存储对象时,通常需要同时重写equals()hashCode()方法,以确保对象的一致性和正确性。


final 在 Java 中有什么作用?

在Java中,final是一个关键字,用于修饰类、方法和变量。

  1. 修饰类:当一个类被声明为final时,它表示该类是最终的,不能被继承。这意味着其他类无法扩展或继承这个final类,从而保护类的完整性和安全性。例如,String类就是一个final类,不能被继承。

  2. 修饰方法:当一个方法被声明为final时,它表示该方法是最终的,不能被子类重写。这样做的目的是为了避免子类修改父类的行为,保持方法的一致性和稳定性。通常,final方法是被认为是不可变的或者具有特殊用途的方法。

  3. 修饰变量:当一个变量被声明为final时,它表示该变量是常量,只能被赋值一次,并且在后续的操作中不能修改其值。一旦被赋值,final变量的值将保持不变。常量通常使用大写字母表示,并且在声明时就需要进行初始化。final变量在多线程环境下具有线程安全性。

final关键字的作用如下:

  • 提供安全性:final关键字可以保护类、方法和变量的完整性,防止被修改或继承,从而提供额外的安全性。
  • 优化性能:对于final方法和变量,编译器可以进行优化,例如内联方法调用和常量折叠,以提高程序的执行效率。
  • 明确意图:使用final关键字可以明确表示类、方法或变量的意图,使代码更易读、理解和维护。

需要注意的是,final关键字并不是必须使用的,它根据需要和设计要求来决定是否使用。在设计类、方法和变量时,考虑使用final关键字可以增加代码的可靠性和可维护性。


Math.round()方法特性

Case1:小数点后第一位 = 5
正数:Math.round(11.5) = 12
负数:Math.round(-11.5) = -11
Case2:小数点后第一位 < 5
正数:Math.round(11.49) = 11
负数:Math.round(-11.49) = -11
Case3:小数点后第一位 > 5
正数:Math.round(11.69) = 12
负数:Math.round(-11.69) = -12

总结:
Math类中提供了三个与取整有关的方法:ceil,floor,round 这些方法的作用于它们的英文名称的含义相对应
例如:ceil的英文意义是天花板,该方法就表示向上取整,Math.ceil(11.3)的结果为12,Math.ceil(-11.6)的结果为-11;
floor的英文是地板,该方法就表示向下取整,Math.floor(11.6)的结果是11,Math.floor(-11.4)的结果-12;
最难掌握的是round方法:他表示“四舍五入”,算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,
所以,Math.round(11.5)的结果是12,Math.round(-11.5)的结果为-11.

  Math.round( )符合这样的规律:小数点后大于5,正数和负数的个位数全部加1,
                                                    小数点后等于5,正数的个位数加1,
                                                    小数点后小于5,正数和负数的个位数全不加1(保持个位数原数)。
                                                    注:这里所说的加1,是先将负号去掉,加完后,再变为负号。

你可能感兴趣的:(java面试题,java,spring,开发语言)