1、重载和重写的区别
重载(overload):发生在一个类中,有两个或者两个以上的方法名相同,其方法参数不同的方法。参数不同可以是参数顺序不同、个数不同、类型不同,在参数不同的基础上,方法的返回类型或者修饰符也可以不同。编译时,java平台会根据传入的参数类型来判断调用哪个方法。
注意:参数顺序相同,而返回类型或者修饰符不同,不是重载。java平台就会不知道该调用哪个方法
public class Overload {
public void doit(int val,String str) {
//方法体
}
public void doit(String str,int val) {
//方法体
}
void doit(int val,String str,String str1) {
//方法体
}
public String doit(int val1,int val2,String str1,String str2) {
return str1;
}
}
重写(override):发生在子类继承父类,子类重写父类的方法,方法名与参数必须相同,可见性(修饰符)必须大于等于父类,返回值必须是父类方法中返回类型的子类或者与父类相同。父类中被private修饰的方法只能在类范围内使用,无法重写,但是可以在子类中重新定义一个与父类private修饰的方法相同的方法,此时不是重写。父类中的静态方法可以重写,但是必须还是静态方法。父类中被final修饰的方法不能重写。
class Parent {
protected void doit1() {
//方法体
}
public Object doit2() {
return new Object();
}
private void doit3() {
//方法体
}
}
class Child extends Parent {
public void doit1() {//可见性大于父类方法
//方法体
}
public String doit2() {
return new String();//返回值为父类方法返回值的子类
}
private void doit3() {//此时不是重写 而是重新定义了一个相同名的方法
//方法体
}
}
2、String、StringBuffer、StringBuilder的区别是什么?
就字符串内容是否可变来说,String类型是不可变的字符串,在String类中,使用了final关键字修饰字符数组,一旦创建就不能被更改。而StringBuffer和StringBuilder都是AbstractStringBuilder类,其字符数组没有被final修饰,是可变的字符串。就线程安全来说,String类型是不可变的,可以说是线程安全的,StringBuffer类中的许多方法均被synchronized关键字修饰,是线程安全的,而StringBuilder中的方法未被synchronized修饰,是线程不安全的。从性能上讲,每次对String类型进行改变时,都会生成一个新的String对象,该引用就会指向新的String对象,StringBuilder和StringBuffer均是对其本身进行操作。操作少量的数据时,推荐使用String类型,单线程时,使用StringBuilder,多线程时,使用StringBufffer。
3、自动装箱与自动拆箱
在java中有八种基本数据类型:char、boolean、byte、short、int、long、float、double,而java是一种面向对象的语言,在很多地方只能使用对象而不是基本数据类型,因此分别封装了这八种基本数据类型的引用类型:Character、Boolean、Byte、short、Integer、Long、Float、Double,为了方便起见便提供了自动封装和自动装箱的功能,
可以利用valueof()方法实现装箱、xxxValue()实现拆箱,以int型为例
int intValue();将Integer值作为int
staic Integer value(int i);//返回指定Integer指定的int值的Integer实例
自动装箱(boxing):把基本数据类型转换为对应的包装类型
自动拆箱(unboxing):将包装类型转化为基本数据类型
int val = 20;
ArrayList<Integer> array = new ArrayList<>();//该数组中只能添加Integer型对象
array.add(val);//int型变量会自动装箱为Integer
val = array.get(0);//Integer型对象又会自动拆箱为int型变量
4、==、equals方法与hashcode方法
==:对于基本数据类型和字符串来说,是比较值是否相等;对于对象类型来说,是比较两个引用是否是指向同一个对象
public class test {
public static void main(String[] args) {
int val1 = 20;
int val2 = 20;
Object obj1 = new Object();
Object obj2 = new Object();
System.out.println(val1 == val2);
System.out.println(obj1 == obj2);
}
}
//输出
true
false
equals()和hashcode()均是Object类中的方法,equals()方法比较的是两个引用是否指向同一个对象,即比较两个引用指向的地址是否相同;hashcode()会根据对象所在的内存地址返回一个能够唯一代表这个对象的int值。
public class test {
public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = new Object();
System.out.println(obj1.equals(obj2));
System.out.println(obj1.hashCode());
System.out.println(obj2.hashCode());
}
}
//输出
false
366712642
1829164700
Object类中的Object类是所有类的父类,因此所有类都会继承方法,但是通常会对equals()和hashCode()这两个方法进行重写,重写equals()后,比较两个对象的内容是否相等,重写hashcode()后会保证两个相同的对象的哈希值相等例如String类就重写了这两个方法。
public class test {
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1.equals(str2));
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
}
}
//输出
true
96354
96354
一般情况下,不涉及hash类型集合时候,只需要重写equals(),不需要重写hashcode(),主要原因是:hashMap、HashSet、HashTable等会通过对象的哈希值来判断两个对象是否相等,所以必须重写hashCode(),以免导致两个两个内容相同的对象被存储两遍。
5、String类两种创建方法的区别
String str1 = "string";//方式一常量式创建
String str2 = "string";
String str3 = new String("string");//方式二对象式创建
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
//输出
true
false
true
true
对于第一种创建方式,在创建字符串常量时,JVM会检查字符串常量池是否存在”string“字符串,如果不存在,就会在常量池中开辟一个内存空间存放"string",变量名str1是在栈中,保存的是“string”的地址;如果存在,在不必再在常量池中创建一个“string”字符串,变量名str2保存的也是“string”的地址,因此str1和str2都是指向的是同一个对象,第一个输出是true(等号是判断两个引用是否是指向同一个对象)
对于第二种创建方式,在创建时,JVM也会先检查字符串常量池是否存在“string”字符串,如果不存在,则会在常量池中开辟一二个内存空间存放“string”,如果不存在,则不需要重新再开辟空间。然后在内存堆中开辟空间存放new出的String实例指向的是”string“,栈中保存的是引用str3指向new的String实例。因此str1是指向”string“的,而str3是指向new String的,第二个输出是false。而equals()在String类中是比较的内容是否相等,因此第三和第四个输出均为true。
6、关于封装
将同类事物的共性封装在一个类中,在类的外部无法直接访问该类,只能通过该类的实例化对象或者类名进行访问。封装遵循“开闭原则”,禁止外部直接访问和修改类的信息,可通过修饰符来限制类和类中方法的可视性。
- 类变量:即静态变量,该变量被static修饰,属于类范畴,只能通过类名进行访问;一旦类变量被修改,那么该类的所有对象中该变量均会被修改
- 成员变量:在类定义时声明的变量,会随着对象的建立而被简历,对象消失也会随之消失,属于对象范畴,存在于对象所在的堆内存中
- 局部变量:在函数中声明的变量,只定义在局部范围,存在于栈内存中,作用的范围结束,栈帧释放,变量也就随之消失了
- 成员方法:声明在类体中的方法,被static修饰的方法,属于类范围,通过类名访问,其余属于对象范围,通过对象访问
- 修饰符
所在类 | 同个包中的类可见 | 对子类可见 | 对不在同个包中的类可见 | |
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
默认 | √ | √ | × | × |
private | √ | × | × | × |
注意:pretected对不是同一个包中不可见,但是若是其子类,是可见的;default对同包中的子类也是可见的
- 构造函数:是用来初始化对象的,通过构造函数创造一个对象,一个类中可以有多个构造函数(重载),构造函数的方法名与类名相同。需要注意的是,在类中若没有声明构造函数,java会默认声明一个无参构造函数;若声明了构造函数,若想使用无参构造则必须自己声明
7、关于继承 extends
继承是类与类的一种关系,子类继承父类所有属性和方法(private修饰的方法不会被继承),以此来实现带代码的复用。
关于继承需要注意的几点问题:
- 父类中private修饰的方法的可见性仅在本类中,因此不会被继承
- 父类中被final修饰的方法,在子类中不能重写
- 子类可以重写父类中的方法,返回值必须与父类中方法的返回值类型相同或者为该类型的子类,修饰符的可见性必须大于等于父类。
- 父类中被final修饰的方法,不能重写
- 父类中被static修饰的方法,若在子类中重写该方法,必须用static修饰
8、关于多态
多态实际上就是同种事物的多种状态,主要包括引用多态和方法多态
- 引用多态:使用父类引用接收子类对象,即向上转型;向下转型,只能通过强制转换,且只有父类的引用指向的是子类的对象,才能够强转。需要注意对象的实际类型决定调用的方法
public class test1 {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Parent parent = new Child();//向上转型
parent.doit();
Child child = (Child)parent;//向下转型,parent实际上是子类对象 才能用子类的引用接收
child.doit();
}
}
class Parent{
public void doit() {
System.out.println("父类方法");
}
}
class Child extends Parent{
public void doit() {
System.out.println("子类重写父类中的方法");
}
}
//输出
子类重写父类中的方法
子类重写父类中的方法
- 方法多态:主要是子类重写父类中的方法,会根据引用所指向的对象决定调用子类还是父类中的方法;重载:根据参数决定调用哪一个方法
9、关于接口 implements interface(JDK8)
由于在java中只能单继承,为了弥补该缺陷,引入接口,接口可以多继承。接口可以有成员变量、局部变量和成员方法
- 成员变量:默认被final修饰,同时也能被public 和 static修饰,用static修饰时,只能通过接口名来访问
- 局部变量:定义在方法体内的变量
- 成员方法:若没有方法体,则默认为抽象方法;若有方法体,则可以被default、static修饰,被statci修饰时,只能通过接口名访问
public interface Interface {
int val1 = 20;//默认被final修饰
public int val2 = 30;
static int val3 = 40;//仅能通过接口访问
public default void doit1() {
System.out.println("接口中的default方法,通过接口实现类对象访问");
}
public static void doit2() {
System.out.println("接口中的静态方法,只能通过接口名访问");
}
public abstract void doit3();
public void doit4();//默认为抽象方法
}
注意:通过类实现接口,类中如果没有实现接口中的方法,那么这个类必须变成抽象类,否则就添加要实现的方法
10、抽象类
抽象类是被abstract修饰的类,相比于普通类,区别在于:抽象类可以定义抽象方法,定义了抽象方法的类一定是抽象类;
抽象类不能实例化对象。与接口相似,继承抽象类后,要么实现其中的抽象方法,要么将其变为抽象类。
特点如下:
- 抽象的
- 抽象类不一定有抽象方法,但类中有抽象方法,只能是抽象类
- 抽象类不能new创建对象,但抽象类的引用可以接收实现了其抽象方法的子类对象
- 使用关键字abstract修饰抽象方法和抽象类
- 若其子类不重写抽象方法,那么子类也必须是抽象方法
11、java中基本类型与引用类型的区别
基本类型:char、boolean、byte、short、int、long、float、double,保存的直接是原始值
引用类型:保存的是引用值,是指实例对象在堆中的地址
由于Java是面向对象的语言,java为八大基本类型封装了对应的引用类型,以及自动拆箱和自动装箱。
12、static关键字
- 修饰变量:静态变量,在类加载时完成初始化
- 修饰方法:在类加载时就存在,可以访问静态变量,但是不能访问非静态变量
- 修饰代码块:在类加载完之后就会执行代码块中的内容。执行顺序:父类中的静态代码块->子类静态代码块->父类构造方法->子类非静态代码块->子类构造方法
被static修饰的方法和变量称为静态方法和静态变量,属于类范围,一般情况下,通过类名进行访问。静态方法和静态变量是在类加载时决定的,因此在static方法中,是不能访问非静态方法和变量的。需要注意的是,在子类重写父类中的静态方法时,该方法也只能是静态的。静态代码块也将只执行一次。静态代码块是在类加载时执行的
public class staticTest {
public static void main(String[] args){
A obj = new A();
A.doit();
}
}
class A{
static int value = 20;
int value2 = 10;
//静态代码块
static{
System.out.println("测试静态代码块");
}
//静态方法
public static void doit(){
int value = A.value;//在静态方法中可以访问静态变量,但是不能访问非静态变量
System.out.println("测试静态方法");
}
//普通方法
public void doit1(){
System.out.println("测试普通方法");
}
}
//运行结果
测试静态代码块
测试静态方法
测试普通方法
13、final关键字
- 修饰变量:定义时候完成赋值,且只能赋值一次,不能再修改
- 修饰方法:不能被重写
- 修饰类:不能被继承
- 修饰形参:不可变
优点:
- JVM和Java应用都会缓存final变量,提高了性能
- 可以安全的在多线程环境下进行共享,而不需要额外的同步开销
- 使用final关键字,JVM会对方法、变量及类进行优化