Java基础知识面试题3
创建一个对象用什么关键字?对象实例与对象引用有何不同?
new关键字
对象实例与对象引用有何不同?
new创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。
在Java中,一个对象引用可以指向0个或1个对象[一个对象引用可以为null,表示它没有指向任何对象]。同时,一个对象可以有n个引用指向它,也就是说,多个对象引用可以指向同一个对象实例。这种情况下,我们可以通过任意一个对象引用来访问该对象实例的数据和方法。这种多个引用指向同一个对象实例的情况,在Java中被称为对象的共享(ObjectSharing)。对象的共享可以节省内存空间,提高程序的效率
解释java中的变量、成员变量、局部变量的概念
在Java中,变量是指在内存中分配的一块空间,用于存储数据。Java中的变量分为以下两种类型:
1.局部变量:定义在方法、代码块或者方法的参数列表中,只在定义它的方法、代码块或者参数列表中有效,出了这个范围就无法访问。局部变量必须显式地初始化,否则编译器会报错。
2.成员变量:定义在类中,方法外,可以被类中的所有方法访问。成员变量有默认值,如果没有显式地初始化,会根据类型自动赋予默认值。成员变量可以有不同的访问权限,例如public、private、protected和默认的访问权限。
成员变量与局部变量的区别有哪些
成员变量和局部变量是Java中的两种变量,它们有以下区别:
定义位置:成员变量定义在类中,方法外;局部变量定义在方法中、代码块中或者方法的参数列表中。
作用域:成员变量的作用域是整个类,可以被类中的所有方法访问;局部变量的作用域仅限于定义它的方法、代码块或者参数列表。
生命周期:成员变量的生命周期与对象的生命周期一样,当对象被创建时,成员变量就被创建;当对象被销毁时,成员变量也被销毁。局部变量的生命周期在方法、代码块或者参数列表执行完毕后就结束了。
默认值:成员变量有默认值,例如int类型的成员变量默认值为0;局部变量没有默认值,必须在使用之前进行初始化。
访问权限:成员变量可以有不同的访问权限,例如public、private、protected和默认的访问权限;局部变量没有访问权限的概念。
内存分配:成员变量在对象创建时分配内存空间;局部变量在定义时分配内存空间,当作用域结束时,内存空间被释放。
使用原则
在Java中,使用变量时需要遵循就近原则(NearbyPrinciple),即优先使用离当前位置最近的变量。就近原则的具体实现方式如下:
1.首先在当前作用域(方法、代码块、参数列表)中查找变量,如果找到了同名的变量,就使用它。
2.如果在当前作用域中没有找到同名的变量,就在外层作用域(如果有的话)中查找同名的变量,直到找到为止。
3.如果在所有作用域中都没有找到同名的变量,就会报编译错误。
需要注意的是,如果局部变量和成员变量同名,就近原则会优先使用局部变量,如果要使用成员变量,需要使用this关键字来指定。
在Java中定义没有参数的构造方法(无参构造)的作用
在Java中,定义没有参数的构造方法的作用主要有以下两个方面:
1.初始化对象的成员变量:Java中的无参构造方法可以在对象创建时对成员变量进行初始化,确保对象在使用之前具有合理的状态。如果一个类定义了有参构造方法,那么子类必须显式地调用父类的有参构造方法,以确保父类的成员变量得到了初始化。
2.方便对象的创建:定义无参构造方法可以方便对象的创建,不需要在创建对象时传入参数。这在某些场景下非常有用,例如创建一个默认配置的对象或者创建一个对象池。
需要注意的是,如果一个类没有定义构造方法,Java会提供默认的无参构造方法;如果一个类定义了有参构造方法,但是没有定义无参构造方法,那么在创建对象时必须传入参数,否则编译器会报错。因此,无论是否需要在构造方法中进行初始化操作,都建议定义一个无参构造方法,以便在需要时使用。
在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
在Java中,如果一个类没有定义任何构造方法,Java会提供默认的无参构造方法。如果一个类定义了构造方法,但是没有定义无参构造方法,那么在创建对象时必须调用有参构造方法,并传入必要的参数。如果一个子类没有显式地调用父类的构造方法,那么编译器会自动在子类的构造方法中插入一条调用父类无参构造方法的语句,即super。
调用父类无参构造方法的目的是确保父类的成员变量在子类对象创建之前得到了初始化。如果父类没有定义无参构造方法,而子类又没有显式地调用父类的有参构造方法,那么编译器就会报错。因此,如果父类定义了有参构造方法,那么子类必须显式地调用父类的有参构造方法,以确保父类的成员变量得到了初始化。
总之,调用父类无参构造方法的目的是确保父类的成员变量在子类对象创建之前得到了初始化,如果父类没有定义无参构造方法,那么子类必须显式地调用父类的有参构造方法。
一个类的构造方法的作用是什么?若一个类没有声明构造方法,程序能正确执行吗?为什么?
一个类的构造方法的作用是什么?
一个类的构造方法的作用是在创建对象时对对象进行初始化,可以用来初始化对象的成员变量,确保对象在使用之前具有合理的状态。
骚戴理解:对于合理的状态的理解如下
在Java中,一个对象的状态是由其成员变量的值决定的。一个对象处于合理的状态意味着它的成员变量具有合理的值,能够正常地执行其设计的功能。例如,一个Person类的对象,在创建时应该具有合理的姓名、年龄、性别等成员变量的值,以便在程序中正确地使用这些信息。
构造方法的作用之一就是确保对象在创建时处于合理的状态。通过在构造方法中对成员变量进行初始化,可以确保对象在使用之前具有合理的状态,避免出现未初始化或不合理的状态。例如,如果一个Person类的对象没有初始化姓名、年龄、性别等成员变量,那么在程序中使用这些信息时就会出现错误,导致程序崩溃或产生不正确的结果。
因此,一个类的构造方法的作用是确保对象在创建时处于合理的状态,即其成员变量具有合理的值,能够正常地执行其设计的功能。
若一个类没有声明构造方法,程序能正确执行吗?为什么?
可以执行。因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。
构造方法有哪些特性?
Java中的构造方法有以下几个特性:
1.构造方法的名称必须与类名相同,且没有返回类型,包括void类型。
2.构造方法可以有不同的参数列表,包括无参构造方法、有参构造方法以及重载构造方法等。
3.如果一个类没有定义任何构造方法,Java会提供默认的无参构造方法,如果定义了构造方法,但没有定义无参构造方法,那么在创建对象时必须调用有参构造方法,并传入必要的参数。
4.构造方法在创建对象时自动调用,用于初始化对象的成员变量,确保对象在使用之前具有合理的状态。
5.构造方法可以调用其他方法或父类的构造方法,并可以使用this和super关键字来访问当前对象和父类对象。
6.如果一个子类没有显式地调用父类的构造方法,那么编译器会自动在子类的构造方法中插入一条调用父类无参构造方法的语句,即super。
7.如果一个类定义了有参构造方法,那么子类必须显式地调用父类的有参构造方法,以确保父类的成员变量得到了初始化。
总之,构造方法是一种特殊的方法,用于在创建对象时对对象进行初始化。构造方法的名称必须与类名相同,没有返回类型,可以有不同的参数列表。构造方法可以调用其他方法或父类的构造方法,并可以使用this和super关键字来访问当前对象和父类对象。在使用构造方法时需要注意调用顺序和传入参数的正确性,以确保对象在使用之前处于合理的状态。
骚戴理解:这里我一直以为构造器只能是public修饰,这是错误的!
静态变量和实例变量(普通变量)区别
静态变量:静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。
实例变量(普通变量):每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。
静态方法和实例方法有何不同?
1、在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
2、静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),实例方法则无此限制
在一个静态方法内调用一个非静态成员为什么是非法的?
在一个静态方法内调用一个非静态成员是非法的,因为静态方法和非静态成员是属于不同的类成员,它们的访问方式和作用域不同。
静态方法是属于类的方法,它不依赖于任何对象的实例,可以直接通过类名调用。静态方法只能访问静态成员,因为静态成员在类加载时已经被初始化,不依赖于对象的实例。
非静态成员是属于对象的成员,它必须通过对象的实例来访问。非静态成员只能在对象创建后才能被访问,因为它们的值是与对象的实例相关的。
因此,在一个静态方法内调用一个非静态成员是非法的,因为静态方法不依赖于对象的实例,不能直接访问非静态成员。如果需要在静态方法中访问非静态成员,可以通过创建对象的实例来访问,或者将非静态成员改为静态成员。
什么是方法的返回值?返回值的作用是什么?
方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。
返回值的作用:接收方法产生的结果,使得它可以用于其他的操作!
什么是内部类?
在Java中,可以将一个类的定义放在另外一个类的定义内部,这就是内部类。
内部类本身就是类的一个属性,与其他属性定义方式一致。
内部类的分类有哪些
Java中的内部类可以分为以下四种:
1.成员内部类(MemberInnerClass):成员内部类是定义在另一个类中的类,它可以访问外部类的所有成员变量和方法,包括私有成员。成员内部类可以被声明为static或非static,如果声明为static,则称为静态内部类。
2.局部内部类(LocalInnerClass):局部内部类是定义在方法或作用域内的类,它只能在该方法或作用域内访问,不能被其他方法或作用域访问。局部内部类可以访问外部类的所有成员变量和方法,但是只能访问final或effectivelyfinal的局部变量。
3.匿名内部类(AnonymousInnerClass):匿名内部类是没有类名的内部类,它通常用于创建实现了某个接口或继承了某个类的对象。匿名内部类可以访问外部类的所有成员变量和方法,但是不能定义构造方法,因为它没有类名。
4.静态内部类(StaticInnerClass):静态内部类是定义在另一个类中的static类,它不能访问外部类的非static成员,只能访问外部类的static成员。静态内部类可以被直接访问,不需要先创建外部类的对象。
1、静态内部类
定义在类内部的静态类,就是静态内部类。
publicclassOuter{privatestaticintradius=1;//静态内部类staticclassStaticInner{publicvoidvisit{System.out.println("visitouterstaticvariable:"+radius);}}}
静态内部类可以访问外部类所有的静态变量,但是不可访问外部类的非静态变量;
静态内部类的创建方式,new外部类.静态内部类,如下:
Outer.StaticInnerinner=newOuter.StaticInner;
inner.visit;
骚戴理解:静态内部类里面定义的方法不一定都是静态方法。静态内部类是一个独立的类,它可以包含静态成员和非静态成员,也可以包含静态方法和非静态方法
2、成员内部类
定义在类内部,成员位置上的非静态类,就是成员内部类。
publicclassOuter{privatestaticintradius=1;privateintcount=2;classInner{publicvoidvisit{System.out.println("visitouterstaticvariable:"+radius);System.out.println("visitoutervariable:"+count);}}}
成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。
成员内部类依赖于外部类的实例,它的创建方式外部类实例.new内部类,如下:
Outerouter=newOuter;
Outer.Innerinner=outer.newInner;
inner.visit;
骚戴理解:可以看出静态内部类和成员内部类在定义方面只是差了一个static
3、局部内部类
定义在方法中的内部类,就是局部内部类。
publicclassOuter{privateintout_a=1;privatestaticintSTATIC_b=2;publicvoidtestFunctionClass{intinner_c=3;classInner{privatevoidfun{System.out.println(out_a);System.out.println(STATIC_b);System.out.println(inner_c);}}Innerinner=newInner;//直接在方法里用new创建局部内部类inner.fun;}publicstaticvoidtestStaticFunctionClass{intd=3;classInner{privatevoidfun{//System.out.println(out_a);//编译错误,定义在静态方法中的局部类不可以访问外部类的实例变量System.out.println(STATIC_b);System.out.println(d);}}Innerinner=newInner;//直接在方法里用new创建局部内部类inner.fun;}}
定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和静态方法。
局部内部类的创建方式,在对应方法内new内部类,如下:
publicstaticvoidtestStaticFunctionClass{classInner{}Innerinner=newInner;}
4、匿名内部类
匿名内部类就是没有名字的内部类
匿名内部类举例
红色框画出来的就是一个匿名内部类同时重写了父类Animals的eat方法,并且调用了这个匿名内部类的eat方法
如何调用匿名内部类中的方法?
1、匿名内部类中只有一个方法的情况
2、匿名内部类有多个方法
第一种方式
第二种方式
如果想调用匿名内部类自己特有的方法的时候呢?该如何调用呢?
骚戴理解:简单来说就是这个实现不了,匿名内部类无法调用自己特有的接口,直接调用会报错,它只能调用继承的类或实现的接口里有的方法,例如上面的run方法就是上面匿名内部类特有的方法,这样的方法是无法调用的
匿名内部类实现接口
匿名内部类存在的前提是要有继承或者实现关系的,但是并没有看到extends和implements关键字,这是怎么回事呢?
答:在使用匿名内部类时,确实需要有继承或者实现关系,但是并不需要使用extends和implements关键字来显示地声明继承或实现关系。这是因为在匿名内部类的定义中,已经隐含了继承或实现的关系。这些由jvm搞定了。
匿名内部类的特点
除了没有名字,匿名内部类还有以下特点:
1、匿名内部类必须继承一个抽象类或者实现一个接口。
2、匿名内部类不能定义任何静态成员和静态方法。
3、匿名内部类访问局部变量的时候,必须把局部变量声明为final。
4、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
匿名内部类创建方式:
new类/接口{//匿名内部类实现部分}
内部类的优点
内部类是Java中一种特殊的类,它定义在另一个类的内部。内部类具有以下几个优点:
1.封装性:内部类可以访问外部类的私有成员,而外部类不能访问内部类的私有成员。这样可以实现更好的封装性,防止外部类直接访问内部类的私有成员。
2.继承性:内部类可以继承外部类,而且可以多重继承。这样可以实现更灵活的继承关系
3.多态性:内部类可以实现接口或继承抽象类,从而实现多态性。这样可以更好地支持面向对象编程的特性,如封装、继承和多态。
4.代码组织性:内部类可以将相关的代码组织在一起,从而提高代码的可读性和可维护性。内部类可以访问外部类的成员,从而可以更方便地实现某些功能。
5.回调函数:内部类可以用作回调函数,从而实现更灵活的事件处理机制。内部类可以访问外部类的成员,从而可以更方便地处理事件。
总之,内部类是Java中一种非常有用的特性,它可以提高代码的封装性、继承性、多态性、代码组织性和灵活性,可以帮助我们更好地实现面向对象编程的特性。
内部类有哪些应用场景
内部类是Java中一种特殊的类,它定义在另一个类的内部。内部类具有以下几个应用场景:
1.实现回调函数:内部类可以用作回调函数,从而实现更灵活的事件处理机制。内部类可以访问外部类的成员,从而可以更方便地处理事件。
2.实现多重继承:内部类可以继承外部类,而且可以多重继承。这样可以实现更灵活的继承关系,可以避免多重继承的一些问题。
3.实现接口:内部类可以实现接口或继承抽象类,从而实现多态性。这样可以更好地支持面向对象编程的特性,如封装、继承和多态。
4.封装性:内部类可以访问外部类的私有成员,而外部类不能访问内部类的私有成员。这样可以实现更好的封装性,防止外部类直接访问内部类的私有成员。
5.代码组织性:内部类可以将相关的代码组织在一起,从而提高代码的可读性和可维护性。内部类可以访问外部类的成员,从而可以更方便地实现某些功能。
6.实现迭代器:内部类可以用于实现迭代器,从而实现更灵活的遍历方式。内部类可以访问外部类的成员,从而可以更方便地实现迭代器的功能。
总之,内部类是Java中一种非常有用的特性,它可以提高代码的封装性、继承性、多态性、代码组织性和灵活性,可以帮助我们更好地实现面向对象编程的特性。内部类在实际开发中有着广泛的应用场景。
局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final?
在Java中,局部内部类和匿名内部类访问局部变量时,如果这些变量没有被声明为final,编译器会报错。这是因为Java要求在内部类中访问局部变量时,这些变量必须是final或者是实际上的final。
这是因为内部类和外部类是两个独立的作用域,内部类可以访问外部类的成员变量和方法,但是不能直接访问外部类的局部变量。为了让内部类能够访问外部类的局部变量,Java采用了将这些变量复制一份到内部类中的方式。但是,如果这些变量不是final或实际上的final,那么在复制的过程中,这些变量的值可能会被修改,从而导致内部类和外部类的值不一致。
为了避免这种情况的发生,Java要求在内部类中访问局部变量时,这些变量必须是final或者是实际上的final。这样,在复制变量的值到内部类中时,就可以保证这些变量的值不会被修改,从而保证内部类和外部类的值一致。
需要注意的是,Java8之后,如果变量是final或者是实际上的final,那么就不需要显式地声明为final,编译器会自动将它们视为final变量。但是,如果变量被修改了,编译器仍然会报错。
总之,Java要求在内部类中访问局部变量时,这些变量必须是final或者是实际上的final,这是为了保证内部类和外部类的值一致。
匿名内部类为什么不能定义任何静态成员和静态方法
匿名内部类是一种没有名字的内部类,它是在创建对象时动态生成的一个类。因为匿名内部类没有名字,所以它无法被其他类所引用,也无法被继承或扩展。
静态成员和静态方法是属于类的成员,它们在类加载时就已经被初始化,而不是在对象创建时才被初始化。由于匿名内部类没有名字,它在类加载时无法被初始化静态成员和静态方法,因此不能定义任何静态成员和静态方法。
可以这样理解,静态成员和静态方法是属于类的,而匿名内部类是属于对象的,它只是在创建对象时动态生成的一个类。因此,匿名内部类不能定义静态成员和静态方法,就好像一个人没有名字,就无法拥有自己的财产一样。
总之,匿名内部类是一种没有名字的内部类,它无法被其他类所引用,也无法被继承或扩展。由于它没有名字,无法在类加载时被初始化静态成员和静态方法,因此不能定义任何静态成员和静态方法。
构造器(constructor)是否可被重写(override)
构造器(constructor)不是普通的方法,因此不能像方法一样被重写(override)。构造器是用来创建对象的特殊方法,它在创建对象时被调用,用来初始化对象的成员变量。每个类都有自己的构造器,如果没有显式地定义构造器,Java会自动生成一个默认的构造器。
虽然构造器不能被重写,但是可以通过继承来实现构造器的复用。子类可以使用super关键字调用父类的构造器,从而实现对父类构造器的重用。如果子类没有显式地调用父类的构造器,Java会默认调用父类的无参构造器。如果父类没有无参构造器,子类必须显式地调用父类的带参构造器。
需要注意的是,子类的构造器必须先调用父类的构造器,然后再进行自己的初始化操作。如果子类没有显式地调用父类的构造器,Java会默认调用父类的无参构造器,如果父类没有无参构造器,编译会出错。因此,在使用继承时需要注意构造器的调用顺序和参数传递问题。
重载(Overload)和重写(Override)的区别?
重载(Overload)和重写(Override)是Java中两个重要的概念,它们的区别如下:
1.定义:重载指在同一个类中定义多个同名但参数类型或个数不同的方法;重写指在子类中重新定义父类中已有的方法。
2.参数:重载方法的参数类型或个数必须不同,否则会出现编译错误;重写方法的参数类型和个数必须与父类中被重写的方法相同。
3.返回值:重载方法的返回值类型可以相同也可以不同,但是不能只有返回类型不同;重写方法的返回值类型必须与父类中被重写的方法相同或是其子类。
4.访问修饰符:重载方法可以具有不同的访问修饰符,但是不能比父类中被重载的方法的访问修饰符更严格;重写方法的访问修饰符必须比父类中被重写的方法的访问修饰符更宽松。
5.静态方法:重载可以包括静态方法,但是不能只有静态性质不同;重写方法不能是静态方法。
6.父类和子类:重载方法必须在同一个类中定义;重写方法必须在子类中定义。
重载的方法能否根据返回类型进行区分?
在Java中,重载的方法不能仅根据返回类型进行区分。这是因为Java编译器在重载方法时,只会考虑方法名和参数列表,而不会考虑返回类型。如果两个方法的方法名和参数列表相同,但是返回类型不同,那么编译器会认为这是两个相同的方法,从而导致编译错误。
例如,下面的代码就会导致编译错误:
```publicclassTest{publicvoidfoo{}publicintfoo{return0;}}```
这是因为上面的代码中定义了两个同名、参数列表相同但返回类型不同的方法,编译器无法区分它们,从而导致编译错误。
==和equals的区别是什么
==:它的作用是判断两个对象的地址是不是相等。即判断两个对象是不是同一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)
equals:它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
情况1:如果一个类没有重写“equals”方法,那么它将继承Object类的“equals”方法,该方法比较的是两个对象的内存地址是否相同,即与“==”比较的结果相同
情况2:如果重写了equals方法,那么就是比较内容是否相同,例如String类的equals方法就是重写了的
举个例子:
publicclasstest1{publicstaticvoidmain(String[]args){Stringa=newString("ab");//a为一个引用Stringb=newString("ab");//b为另一个引用,对象的内容一样Stringaa="ab";//放在常量池中Stringbb="ab";//从常量池中查找if(aa==bb)//trueSystem.out.println("aa==bb");if(a==b)//false,非同一对象System.out.println("a==b");if(a.equals(b))//trueSystem.out.println("aEQb");if(42==42.0){//trueSystem.out.println("true");}}}
骚戴理解:String中的equals方法是被重写过的,所以String的equals方法比较的是对象的内容是否相同。
java中常量放在jvm的堆还是方法区
在Java中,常量一般放在方法区中,而不是放在JVM的堆中。方法区是一块用于存储类的元数据、静态变量、常量、类信息等的内存区域,它是线程共享的,与堆一样也是被整个JVM所共享的。
具体来说,Java中的常量包括两种类型:字面常量和符号常量。字面常量是指代码中直接使用的常量值,比如字符串、数字、布尔值等;而符号常量是指使用final关键字定义的常量,它们的值在编译时就已经确定,并且不能被修改。
对于字面常量,它们通常是放在JVM的常量池中的,常量池是方法区的一部分,用于存储编译时生成的各种字面常量和符号引用。常量池中的常量可以被多个线程共享,而且它们的生命周期与类的生命周期相同。
对于符号常量,它们通常是放在方法区的静态变量中的,也就是说,它们的值在类加载时就已经确定,并且在整个程序的生命周期内都不会被修改。
总之,在Java中,常量一般放在方法区中,常量池和静态变量都是方法区的一部分,用于存储常量和静态变量等信息。