黑马程序员——java基础面试题之面向对象

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

成员变量和局部变量的区别?

		A:在类中的位置不同
			成员变量:在类中方法外
			局部变量:在方法定义中或者方法声明上
		B:在内存中的位置不同
			成员变量:在堆内存
			局部变量:在栈内存
		C:生命周期不同
			成员变量:随着对象的创建而存在,随着对象的消失而消失
			局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
		D:初始化值不同
			成员变量:有默认初始化值
			局部变量:没有默认初始化值,必须定义,赋值,然后才能使用。
			
		注意事项:
			局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则。

静态变量和成员变量的区别

		A:所属不同
			静态变量:属于类,类变量
			成员变量:属于对象,对象变量,实例变量
		B:内存位置不同
			静态变量:方法区的静态区
			成员变量:堆内存
		C:生命周期不同
			静态变量:静态变量是随着类的加载而加载,随着类的消失而消失
			成员变量:成员变量是随着对象的创建而存在,随着对象的消失而消失
		D:调用不同
			静态变量:可以通过对象名调用,也可以通过类名调用
			成员变量:只能通过对象名调用
static关键字注意事项

		A:在静态方法中是没有this关键字的
			如何理解呢?
				静态是随着类的加载而加载,this是随着对象的创建而存在。
				静态比对象先存在。
		B:静态方法只能访问静态的成员变量和静态的成员方法
				静态方法:
					成员变量:只能访问静态变量
					成员方法:只能访问静态成员方法
				非静态方法:
					成员变量:可以是静态的,也可以是非静态的
					成员方法:可是是静态的成员方法,也可以是非静态的成员方法。
			简单记:
				静态只能访问静态。
静态代码块,构造代码块,构造方法的执行顺序?

		静态代码块 -- 构造代码块 -- 构造方法
		静态代码块:只执行一次
		构造代码块:每次调用构造方法都执行
/*
	写程序的执行结果。
	
	林青霞都60了,我很伤心
	我是main方法
	Student 静态代码块
	Student 构造代码块
	Student 构造方法
	Student 构造代码块
	Student 构造方法
*/
class Student {
	static {
		System.out.println("Student 静态代码块");
	}
	
	{
		System.out.println("Student 构造代码块");
	}
	
	public Student() {
		System.out.println("Student 构造方法");
	}
}

class StudentDemo {
	static {
		System.out.println("林青霞都60了,我很伤心");
	}
	
	public static void main(String[] args) {
		System.out.println("我是main方法");
		
		Student s1 = new Student();
		Student s2 = new Student();
	}
}
Java中继承的特点
		A:Java只支持单继承,不支持多继承。
			有些语言是支持多继承,格式:extends 类1,类2,...
		B:Java支持多层继承(继承体系)
/*
class Father {}
class Mother {}
class Son exnteds Father {} //正确的
class Son extends Father,Mother {} // 错误的
*/

 继承的注意事项 
  
		A:子类只能继承父类所有非私有的成员(成员方法和成员变量)
		B:子类不能继承父类的构造方法,但是可以通过super(马上讲)关键字去访问父类构造方法。
		C:不要为了部分功能而去继承
			class A {
				public void show1(){}
				public void show2(){}
			}
			
			class B {
				public void show2(){}
				public void show3(){}
			}
			
			//我们发现B类中出现了和A类一样的show2()方法,所以,我们就用继承来体现
			class B extends A {
				public void show3(){}
			}
			这样其实不好,因为这样你不但有了show2(),还多了show1()。
			有可能show1()不是你想要的。
this和super的区别

		分别是什么呢?
			this代表本类对应的引用。
			super代表父类存储空间的标识(可以理解为父类引用,可以操作父类的成员)

		怎么用呢?
			A:调用成员变量
				this.成员变量 调用本类的成员变量
				super.成员变量 调用父类的成员变量
			B:调用构造方法
				this(...)	调用本类的构造方法
				super(...)	调用父类的构造方法
			C:调用成员方法
				this.成员方法 调用本类的成员方法
				super.成员方法 调用父类的成员方法
继承中构造方法的关系

		A:子类中所有的构造方法默认都会访问父类中空参数的构造方法
		B:为什么呢?
			因为子类会继承父类中的数据,可能还会使用父类的数据。
			所以,子类初始化之前,一定要先完成父类数据的初始化。
			
			注意:子类每一个构造方法的第一条语句默认都是:super();
如果父类没有无参构造方法,那么子类的构造方法会出现什么现象呢?

/*
	报错。
	如何解决呢?	
		A:在父类中加一个无参构造方法
		B:通过使用super关键字去显示的调用父类的带参构造方法
		C:子类通过this去调用本类的其他构造方法
			子类中一定要有一个去访问了父类的构造方法,否则父类数据就没有初始化。
			
	注意事项:
		this(...)或者super(...)必须出现在第一条语句上。
		如果不是放在第一条语句上,就可能对父类的数据进行了多次初始化,所以必须放在第一条语句上。
*/
class Father {
	/*
	public Father() {
		System.out.println("Father的无参构造方法");
	}
	*/
	
	public Father(String name) {
		System.out.println("Father的带参构造方法");
	}
}

class Son extends Father {
	public Son() {
		super("随便给");
		System.out.println("Son的无参构造方法");
		//super("随便给");
	}
	
	public Son(String name) {
		//super("随便给");
		this();
		System.out.println("Son的带参构造方法");
	}
}

class ExtendsDemo7 {
	public static void main(String[] args) {
		Son s = new Son();
		System.out.println("----------------");
		Son ss = new Son("林青霞");
	}
}
方法重写的注意事项

		A:父类中私有方法不能被重写
			因为父类私有方法子类根本就无法继承
		B:子类重写父类方法时,访问权限不能更低
			最好就一致
		C:父类静态方法,子类也必须通过静态方法进行重写
			其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中我会讲解
			
		子类重写父类方法的时候,最好声明一模一样。
看程序写结果

/*
	看程序写结果:
		A:成员变量	就近原则
		B:this和super的问题
			this访问本类的成员
			super访问父类的成员
		C:子类构造方法执行前默认先执行父类的无参构造方法
		D:一个类的初始化过程
			成员变量进行初始化
				默认初始化
				显示初始化
				构造方法初始化
				
	结果:
		fu
		zi
		30
		20
		10
*/
class Fu{
	public int num = 10;
	public Fu(){
		System.out.println("fu");
	}
}
class Zi extends Fu{
	public int num = 20;
	public Zi(){
		System.out.println("zi");
	}
	public void show(){
		int num = 30;
		System.out.println(num); //30
		System.out.println(this.num); //20
		System.out.println(super.num); //10
	}
}
class ExtendsTest {
	public static void main(String[] args) {
		Zi z = new Zi();
		z.show();
	}
}
/*
	看程序写结果:
		A:一个类的静态代码块,构造代码块,构造方法的执行流程
			静态代码块 > 构造代码块 > 构造方法
		B:静态的内容是随着类的加载而加载
			静态代码块的内容会优先执行
		C:子类初始化之前先会进行父类的初始化
		
	结果是:
		静态代码块Fu
		静态代码块Zi
		构造代码块Fu
		构造方法Fu
		构造代码块Zi
		构造方法Zi
*/
class Fu {
	static {
		System.out.println("静态代码块Fu");
	}

	{
		System.out.println("构造代码块Fu");
	}

	public Fu() {
		System.out.println("构造方法Fu");
	}
}

class Zi extends Fu {
	static {
		System.out.println("静态代码块Zi");
	}

	{
		System.out.println("构造代码块Zi");
	}

	public Zi() {
		System.out.println("构造方法Zi");
	}
}

class ExtendsTest2 {
	public static void main(String[] args) {
		Zi z = new Zi();
	}
}

/*
	看程序写结果:
		A:成员变量的问题
			int x = 10; //成员变量是基本类型
			Student s = new Student(); //成员变量是引用类型
		B:一个类的初始化过程
			成员变量的初始化
				默认初始化
				显示初始化
				构造方法初始化
		C:子父类的初始化(分层初始化)
			先进行父类初始化,然后进行子类初始化。
			
	结果:
		YXYZ
		
	问题:
		虽然子类中构造方法默认有一个super()
		初始化的时候,不是按照那个顺序进行的。
		而是按照分层初始化进行的。
		它仅仅表示要先初始化父类数据,再初始化子类数据。
*/
class X {
	Y b = new Y();
	X() {
		System.out.print("X");
	}
}

class Y {
	Y() {
		System.out.print("Y");
	}
}

public class Z extends X {
	Y y = new Y();
	Z() {
		//super();
		System.out.print("Z");
	}
	public static void main(String[] args) {
		new Z(); 
	}
}
方法重写和方法重载的区别?方法重载能改变返回值类型吗?

方法重写:
	在子类中,出现和父类中一模一样的方法声明的现象。
	
方法重载:
	同一个类中,出现的方法名相同,参数列表不同的现象。


方法重载能改变返回值类型,因为它和返回值类型无关。


Override:方法重写
Overload:方法重载
this关键字和super关键字分别代表什么?以及他们各自的使用场景和作用。
this:代表当前类的对象引用
super:代表父类存储空间的标识。(可以理解为父类的引用,通过这个东西可以访问父类的成员)

场景:
	成员变量:
		this.成员变量
		super.成员变量
	构造方法:
		this(...)
		super(...)
	成员方法:
		this.成员方法
		super.成员方法
final关键字

/*
	final可以修饰类,方法,变量
	
	特点:
		final可以修饰类,该类不能被继承。
		final可以修饰方法,该方法不能被重写。(覆盖,复写)
		final可以修饰变量,该变量不能被重新赋值。因为这个变量其实常量。
		
	常量:
		A:字面值常量
			"hello",10,true
		B:自定义常量
			final int x = 10;
*/

//final class Fu //无法从最终Fu进行继承

class Fu {
	public int num = 10;
	public final int num2 = 20;

	/*
	public final void show() {
	
	}
	*/
}

class Zi extends Fu {
	// Zi中的show()无法覆盖Fu中的show()
	public void show() {
		num = 100;
		System.out.println(num);
		
		//无法为最终变量num2分配值
		//num2 = 200;
		System.out.println(num2);
	}
}

class FinalDemo {
	public static void main(String[] args) {
		Zi z = new Zi();
		z.show();
	}
}
/*
	面试题:final修饰局部变量的问题
		基本类型:基本类型的值不能发生改变。
		引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。
*/
class Student {
	int age = 10;
}

class FinalTest {
	public static void main(String[] args) {
		//局部变量是基本数据类型
		int x = 10;
		x = 100;
		System.out.println(x);
		final int y = 10;
		//无法为最终变量y分配值
		//y = 100;
		System.out.println(y);
		System.out.println("--------------");
		
		//局部变量是引用数据类型
		Student s = new Student();
		System.out.println(s.age);
		s.age = 100;
		System.out.println(s.age);
		System.out.println("--------------");
		
		final Student ss = new Student();
		System.out.println(ss.age);
		ss.age = 100;
		System.out.println(ss.age);
		
		//重新分配内存空间
		//无法为最终变量ss分配值
		ss = new Student();
	}
}
/*
	final修饰变量的初始化时机
		A:被final修饰的变量只能赋值一次。
		B:在构造方法完毕前。(非静态的常量)
*/
class Demo {
	//int num = 10;
	//final int num2 = 20;
	
	int num;
	final int num2;
	
	{
		//num2 = 10;
	}
	
	public Demo() {
		num = 100;
		//无法为最终变量num2分配值
		num2 = 200;
	}
}

class FinalTest2 {
	public static void main(String[] args) {
		Demo d = new Demo();
		System.out.println(d.num);
		System.out.println(d.num2);
	}
}
/*
	继承的代码体现
	
	由于继承中方法有一个现象:方法重写。
	所以,父类的功能,就会被子类给覆盖调。
	有些时候,我们不想让子类去覆盖掉父类的功能,只能让他使用。
	这个时候,针对这种情况,Java就提供了一个关键字:final
	
	final:最终的意思。常见的是它可以修饰类,方法,变量。
*/
class Fu {
	public final void show() {
		System.out.println("这里是绝密资源,任何人都不能修改");
	}
}

class Zi extends Fu {
	// Zi中的show()无法覆盖Fu中的show()
	public void show() {
		System.out.println("这是一堆垃圾");
	}
}

class ZiDemo {
	public static void main(String[] args) {
		Zi z = new Zi();
		z.show();
	}
}
多态的前提

		A:要有继承关系。
		B:要有方法重写。
			其实没有也是可以的,但是如果没有这个就没有意义。
				动物 d = new 猫();
				d.show();
				动物 d = new 狗();
				d.show();
		C:要有父类引用指向子类对象。
			父 f =  new 子();
多态中的成员访问特点

		A:成员变量
			编译看左边,运行看左边。
		B:构造方法
			创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化。
		C:成员方法
			编译看左边,运行看右边。
		D:静态方法
			编译看左边,运行看左边。
			(静态和类相关,算不上重写,所以,访问还是左边的)
			
		由于成员方法存在方法重写,所以它运行看右边。
class Fu {
	public int num = 100;

	public void show() {
		System.out.println("show Fu");
	}
	
	public static void function() {
		System.out.println("function Fu");
	}
}

class Zi extends Fu {
	public int num = 1000;
	public int num2 = 200;

	public void show() {
		System.out.println("show Zi");
	}
	
	public void method() {
		System.out.println("method zi");
	}
	
	public static void function() {
		System.out.println("function Zi");
	}
}

class DuoTaiDemo {
	public static void main(String[] args) {
		//要有父类引用指向子类对象。
		//父 f =  new 子();
		Fu f = new Zi();
		System.out.println(f.num);
		//找不到符号
		//System.out.println(f.num2);
		
		f.show();
		//找不到符号
		//f.method();
		f.function();
	}
}
多态的好处

		A:提高了代码的维护性(继承保证)
		B:提高了代码的扩展性(由多态保证)
	多态的弊端:
		不能使用子类的特有功能。
 如何实现父类使用子类的特有功能? 
  

		A:创建子类对象调用方法即可。(可以,但是很多时候不合理。而且,太占内存了)
		B:把父类的引用强制转换为子类的引用。(向下转型)
		
	对象间的转型问题:
		向上转型:
			Fu f = new Zi();
		向下转型:
			Zi z = (Zi)f; //要求该f必须是能够转换为Zi的。
ClassCastException:类型转换异常,一般在多态的向下转型中容易出现

class Animal {
	public void eat(){}
}

class Dog extends Animal {
	public void eat() {}
	
	public void lookDoor() {
	
	}
}

class Cat extends Animal {
	public void eat() {
	
	}
	
	public void playGame() {
		
	}
}

class DuoTaiDemo5 {
	public static void main(String[] args) {
		//内存中的是狗
		Animal a = new Dog();
		Dog d = (Dog)a;
		
		//内存中是猫
		a = new Cat();
		Cat c = (Cat)a;
		
		//内存中是猫
		Dog dd = (Dog)a; //ClassCastException
	}
}
看程序写结果

/*
	看程序写结果:先判断有没有问题,如果没有,写出结果
*/
class Fu {
	public void show() {
		System.out.println("fu show");
	}
}

class Zi extends Fu {
	public void show() {
		System.out.println("zi show");
	}

	public void method() {
		System.out.println("zi method");
	}
}

class DuoTaiTest3 {
	public static void main(String[] args) {
		Fu f = new Zi();
		//找不到符号
		//f.method();
		f.show();
	}
}
/*
	看程序写结果:先判断有没有问题,如果没有,写出结果
	
	多态的成员访问特点:
		方法:编译看左边,运行看右边。
		
	继承的时候:
		子类中有和父类中一样的方法,叫重写。
		子类中没有父亲中出现过的方法,方法就被继承过来了。
*/
class A {
	public void show() {
		show2();
	}
	public void show2() {
		System.out.println("我");
	}
}
class B extends A {
	/*
	public void show() {
		show2();
	}
	*/

	public void show2() {
		System.out.println("爱");
	}
}
class C extends B {
	public void show() {
		super.show();
	}
	public void show2() {
		System.out.println("你");
	}
}
public class DuoTaiTest4 {
	public static void main(String[] args) {
		A a = new B();
		a.show();
		
		B b = new C();
		b.show();
	}
}
抽象类的特点

		A:抽象类和抽象方法必须用abstract关键字修饰
		B:抽象类中不一定有抽象方法,但是有抽象方法的类必须定义为抽象类
		C:抽象类不能实例化
			因为它不是具体的。
			抽象类有构造方法,但是不能实例化?构造方法的作用是什么呢?
			用于子类访问父类数据的初始化
		D:抽象的子类
			a:如果不想重写抽象方法,该子类是一个抽象类。
			b:重写所有的抽象方法,这个时候子类是一个具体的类。
			
		抽象类的实例化其实是靠具体的子类实现的。是多态的方式。
			Animal a = new Cat();
抽象类的成员特点

		成员变量:既可以是变量,也可以是常量。
		构造方法:有。
					用于子类访问父类数据的初始化。
		成员方法:既可以是抽象的,也可以是非抽象的。
		
	抽象类的成员方法特性:
		A:抽象方法 强制要求子类做的事情。
		B:非抽象方法 子类继承的事情,提高代码复用性。
abstract不能和哪些关键字共存

	private	冲突
	final	冲突	
	static	无意义
接口的特点

		A:接口用关键字interface表示	
			interface 接口名 {}
		B:类实现接口用implements表示
			class 类名 implements 接口名 {}
		C:接口不能实例化
			那么,接口如何实例化呢?
			按照多态的方式来实例化。
		D:接口的子类
			a:可以是抽象类。但是意义不大。
			b:可以是具体类。要重写接口中的所有抽象方法。(推荐方案)
接口成员特点

		成员变量;只能是常量,并且是静态的。
				默认修饰符:public static final
				建议:自己手动给出。
		构造方法:接口没有构造方法。
		成员方法:只能是抽象方法。
				默认修饰符:public abstract
				建议:自己手动给出。
interface Inter {
	public int num = 10;
	public final int num2 = 20;
	public static final int num3 = 30;
	
	//错误: 需要<标识符>
	//public Inter() {}
	
	//接口方法不能带有主体
	//public void show() {}

	//abstract void show(); //默认public
	public void show(); //默认abstract
}

//接口名+Impl这种格式是接口的实现类格式
/*
class InterImpl implements Inter {
	public InterImpl() {
		super();
	}
}
*/

class InterImpl extends Object implements Inter {
	public InterImpl() {
		super();
	}
	
	public void show() {}
}

//测试类
class InterfaceDemo2 {
	public static void main(String[] args) {
		//创建对象
		Inter i = new InterImpl();
		System.out.println(i.num);
		System.out.println(i.num2);
		//i.num = 100;
		//i.num2 = 200;
		//System.out.println(i.num); //无法为最终变量num分配值
		//System.out.println(i.num2);//无法为最终变量num2分配值
		System.out.println(Inter.num);
		System.out.println(Inter.num2);
		System.out.println("--------------");
	}
修饰符

	修饰符:
		权限修饰符:private,默认的,protected,public
		状态修饰符:static,final
		抽象修饰符:abstract
		
	类:
		权限修饰符:默认修饰符,public
		状态修饰符:final
		抽象修饰符:abstract
		
		用的最多的就是:public
		
	成员变量:
		权限修饰符:private,默认的,protected,public
		状态修饰符:static,final
		
		用的最多的就是:private
		
	构造方法:
		权限修饰符:private,默认的,protected,public
		
		用的最多的就是:public
		
	成员方法:
		权限修饰符:private,默认的,protected,public
		状态修饰符:static,final
		抽象修饰符:abstract
		
		用的最多的就是:public
		
	除此以外的组合规则:
		成员变量:public static final
		成员方法:public static 
		          public abstract
				  public final
成员内部类

/*
	成员内部类:
		如何直接访问内部类的成员。
		外部类名.内部类名 对象名 = 外部类对象.内部类对象;
*/
class Outer {
	private int num = 10;
	
	class Inner {
		public void show() {
			System.out.println(num);
		}
	}
}

class InnerClassDemo3 {
	public static void main(String[] args) {
		//需求:我要访问Inner类的show()方法
		//Inner i = new Inner();
		//i.show();
		
		//格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
		Outer.Inner oi = new Outer().new Inner();
		oi.show();
	}
}
局部内部类

/*
	局部内部类
		A:可以直接访问外部类的成员
		B:在局部位置,可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能
	
	面试题:
		局部内部类访问局部变量的注意事项?
		A:局部内部类访问局部变量必须用final修饰
		B:为什么呢?
			局部变量是随着方法的调用而调用,随着调用完毕而消失。
			而堆内存的内容并不会立即消失。所以,我们加final修饰。
			加入final修饰后,这个变量就成了常量。既然是常量。你消失了。
			我在内存中存储的是数据20,所以,我还是有数据在使用。
*/
class Outer {
	private int num  = 10;
	
	public void method() {
		//int num2 = 20;
		//final int num2 = 20;
		class Inner {
			public void show() {
				System.out.println(num);
				//从内部类中访问本地变量num2; 需要被声明为最终类型
				System.out.println(num2);//20
			}
		}
		
		//System.out.println(num2);
		
		Inner i = new Inner();
		i.show();
	}
}

class InnerClassDemo5 {
	public static void main(String[] args) {
		Outer o = new Outer();
		o.method();
	}
}
匿名内部类

/*
	匿名内部类
		就是内部类的简化写法。

	前提:存在一个类或者接口
		这里的类可以是具体类也可以是抽象类。
	
	格式:
		new 类名或者接口名(){
			重写方法;
		}
		
	本质是什么呢?
		是一个继承了该类或者实现了该接口的子类匿名对象。
*/
interface Inter {
	public abstract void show();
	public abstract void show2();
}

class Outer {
	public void method() {
		//一个方法的时候
		/*
		new Inter() {
			public void show() {
				System.out.println("show");
			}
		}.show();
		*/
		
		//二个方法的时候
		/*
		new Inter() {
			public void show() {
				System.out.println("show");
			}
			
			public void show2() {
				System.out.println("show2");
			}
		}.show();
		
		new Inter() {
			public void show() {
				System.out.println("show");
			}
			
			public void show2() {
				System.out.println("show2");
			}
		}.show2();
		*/
		
		//如果我是很多个方法,就很麻烦了
		//那么,我们有没有改进的方案呢?
		Inter i = new Inter() { //多态
			public void show() {
				System.out.println("show");
			}
			
			public void show2() {
				System.out.println("show2");
			}
		};
		
		i.show();
		i.show2();
	}
}

class InnerClassDemo6 {
	public static void main(String[] args) {
		Outer o = new Outer();
		o.method();
	}
}

按照要求,补齐代码

/*
	匿名内部类面试题:
		按照要求,补齐代码
			interface Inter { void show(); }
			class Outer { //补齐代码 }
			class OuterDemo {
				public static void main(String[] args) {
					  Outer.method().show();
				  }
			}
			要求在控制台输出”HelloWorld”
*/
interface Inter { 
	void show(); 
	//public abstract
}

class Outer { 
	//补齐代码
	public static Inter method() {
		//子类对象 -- 子类匿名对象
		return new Inter() {
			public void show() {
				System.out.println("HelloWorld");
			}
		};
	}
}

class OuterDemo {
	public static void main(String[] args) {
		Outer.method().show();
		/*
			1:Outer.method()可以看出method()应该是Outer中的一个静态方法。
			2:Outer.method().show()可以看出method()方法的返回值是一个对象。
				又由于接口Inter中有一个show()方法,所以我认为method()方法的返回值类型是一个接口。
		*/
	}
}
面试题
/*
	面试题:
		要求请填空分别输出30,20,10。
		
	注意:
		1:内部类和外部类没有继承关系。
		2:通过外部类名限定this对象
			Outer.this
*/
class Outer {
	public int num = 10;
	class Inner {
		public int num = 20;
		public void show() {
			int num = 30;
			System.out.println(num);
			System.out.println(this.num);
			//System.out.println(new Outer().num);
			System.out.println(Outer.this.num);
		}
	}
}
class InnerClassTest {
	public static void main(String[] args) {
		Outer.Inner oi = new Outer().new Inner();
		oi.show();
	}	
}
晕。。。。。终于收集完了


你可能感兴趣的:(java基础,经典案例)