Java程序员面试宝典笔记记录——4.1.2-4.1.10

4.1.2 下列说法错误的有( )

Java面向对象语言容许单独的过程与函数存在
Java面向对象语言容许单独的方法存在
Java语言中的方法属于类中的成员(member)
Java语言中的方法必定隶属于某一类(对象),调用方法与过程或函数相同

答案:ABC
java不允许单独的方法,过程或函数存在,需要隶属于某一类中。——AB错
java语言中的方法属于对象的成员,而不是类的成员。不过,其中静态方法属于类的成员。——C错

4.1.3为什么需要public static void main(String []args)方法?

该方法是Java程序入口方法,JVM运行程序会先查找main()方法。public是修饰符,表示任何类和对象都可以访问,static表明是静态方法,即方法中代码存储在静态存储区,类被加载后,就可以使用该方法而不需要通过实例化对象来访问,直接类名.main()直接访问,JVM启动便是按照上述方法签名(必须有public和static修饰,返还值为void,且方法参数是字符串数组)来查找方法入口,若查到则执行,否则报错。

/*
实现了一个Java的HelloWorld的程序
实现步骤:
	1.定义类
	2.定义主方法
	3.一条命令,控制台输出了HelloWorld
*/
public class HelloWorld1{   						//	1.定义类
	//main是程序的主方法,固定格式,程序的入口
	public static void main(String[] args){		//2.定义主方法
		for (int i = 0; i < args.length; i++) {
			System.out.println(args[i]);
		}
	}
}

在这里插入图片描述

引申:main方法还有其他定义格式?

public和static没有先后顺序,可以static public void main(String []args)

可以把main()方法定义为final

也可以用synchronized修饰

只要保证返还值void,有static public关键字,不能abstract关键字修饰。

package cn.itcast.demo;
/*
 * 4.1.3
 */
class T{
	public static void main(String[] args) {
		System.out.println("T main");
	}
}
public class Test {
	//程序入口函数2
	public static void main(String[] args) {
		System.out.println("Test main");
	}
}

在这里插入图片描述

在Java中的abstract关键字

Abstract,即抽象。笼统的描述一类东西。
特点:
abstract可以修饰类,也可以修饰方法;
abstract修饰的类可以被继承和实现,但是这个类本身不能被实例化;
抽象方法必须在抽象类中,抽象类中可以有非抽象的方法;
抽象类中可以没有抽象方法,这样做的作用是这个类不可以被实例化;
抽象类的子类在继承抽象类后,必须把父类中的抽象方法重写,否则子类也会自动变为抽象类;
抽象方法用于被子类继承后重写。即是父类的抽象方法是空的,必须由子类继承之后在子类中由子类实现,这可以达到不同子类可以根据自己的特性来定义自己从父类继承过来的抽象方法;
abstract不能与final关键字共存,因为final关键字修饰的类是不能被继承的;
abstract不能与private关键字共存,因为私有的东西不能继承;
abstract不能与static关键字共存,当static修饰方法时,该方法可以通过类名直接调用,而abstract修饰的抽象方法是不能被调用的;
abstract修饰的类中的普通方法可以通过该抽象类的子类直接调用。

抽象类与interface关键字:

当一个抽象类中的所有成员只有抽象方法时,便是一个接口,可以用关键字interface表示;
接口中的成员有两种:1.全局常量 2.抽象方法 ;
接口中的权限必须是public,若没写public,会自动加上public;
接口中有抽象方法,因此接口也是抽象类,不可以实例化。所以其子类也必须覆盖了接口中所有的抽象方法后,才能实例化,否则他也是一个抽象类。

4.1.4 如何实现在main()方法执行前输出"Hello World"

package cn.itcast.demo;
/*
 * 4.1.4
 */
public class Test1 {
	//由于静态块不管顺序如何,都会在main()方法执行之前执行
	static{
		System.out.println("Hello World1");
	}
		
	public static void main(String[] args) {
		System.out.println("Hello World2");
	}
	
	static{
		System.out.println("Hello World3");
	}

}

在这里插入图片描述

4.1.5Java程序初始化顺序?

Java初始化遵循三原则(优先级依次递减)

静态对象(变量)优先于非静态对象(变量)初始化。静态对象(变量)只初始化一次,非静态对象(变量)可多次初始化
父类优先于子类初始化
按照成员变量定义顺序进行初始化,即使变量定义散布在方法定义中,依旧在任何方法(包括构造函数)调用之前先初始化。

Java初始化可以在不同代码块完成,执行顺序如下:

在这里插入图片描述

package cn.itcast.demo1;

public class Base {
	static{
		System.out.println("Base static block 1.父类静态变量 静态代码块");
	}
	
	{
		System.out.println("Base block 3.父类非静态变量 非静态代码块");
	}
	
	public Base(){
		System.out.println("Base constructor 4.父类构造函数");
	}
}

package cn.itcast.demo1;

public class Derived extends Base{
	static{
		System.out.println("Derived static block 2.子类静态变量 静态代码块");
	}
	
	{
		System.out.println("Derived block 5.子类非静态变量 非静态代码块");
	}
	
	public Derived(){
		System.out.println("Derived constructor 6.子类构造函数");
	}
}

package cn.itcast.demo1;

public class text {
	public static void main(String[] args) {
		new Derived();
	}
}

在这里插入图片描述

4.1.6 Java作用域有哪些?

作用域由花括号位置决定,决定了变量名可见性和生命周期。**Java变量类型三种:成员变量、静态变量和局部变量。**类的成员变量作用范围与类的实例化对象作用范围相同,当类被实例化,成员变量会在内存中分配空间并初始化,直到实例化对象生命周期结束,其生命周期才结束。

被static修饰成员变量被称为静态变量或全局变量,但静态变量不依赖于特定实例而是所有实例。只要一个类被加载,JVM就会给类静态变量分配空间,可以类名和变量名来访问。局部变量作用域与可见性为它所在的花括号内。

四种可见性区别

在这里插入图片描述

private和protected不能修饰外部类的原因

大家都知道类的成员变量和方法可以使用private和protected修饰。

使用private修饰,表示该类的成员只能在类的内部访问。

使用protected修饰,表示该类的成员可以被类的内部、同包下的其它类以及该类的子类访问。

从组织结构来分析:

类的成员(包过变量、方法、内部类等)的上层结构是类,而类的上层结构是包。

如果类可以使用private来修饰,表示该包下的这个类不能被其它类访问,那么该类也失去了存在的意义,所以不能使用private来修饰类。
如果类可以使用protected来修饰,表示该类所在的包的其它类可以访问该类;该类所在的包的子包的类可以访问该类,但是包没有继承的概念,所以后一句是不对。所以用protected来修饰类也是没有意义的。

综上,类只有public和default修饰。

4.1.7 一个Java文件中是否可以定义多个类?

可以定义多个类,只能一个类被public修饰且与文件名必须相同。若文件无Public,则文件名是随便一个类名字即可。

package cn.itcast.demo1;

class Base1{
	public void print(){
		System.out.println("Base");
	}
}
public class Derived1 extends Base1{

	public static void main(String[] args) {
		Base1 c = new Derived1();
		c.print();
	}

}

在这里插入图片描述

4.1.8什么是构造函数?

是特殊函数用于对象实例化初始化对象成员变量。

具备特点:

与类名相同无返还值;
可有多个,若不提供构造函数则提供默认,若提供则不创建默认;
可有0,1及1个以上参数;
伴随new调用,不由程序编写者直接调用,经由系统调用。
对象实例时调用仅一次;
完成对象初始化构造;
不能被继承不能被覆盖能重载;
子类可通过super显式调用,父类不提供无参构造函数则子类需要显式调用,反之可以不显式。优先执行父类构造后子类构造;
子父类均无定义构造函数,均生成默认无参构造,修饰符与当前类修饰符一致。

普通的方法可不可以和构造方法同名呢?

答案是可以,但是编译器不会建议我们这么做。它会给我们一个警告。
在这里插入图片描述

package cn.itcast.demo1;

public class Text1 {
	public Text1(){
		System.out.println("construct");
	}
	
	public void Text1(){
		System.out.println("call Text1");
	}
	public static void main(String[] args) {
		Text1 t = new Text1();//调用构造函数
		t.Text1();//调用Text1方法
	}

}

在这里插入图片描述

4.1.9Java为什么某些接口无任何方法?

由于Java不支持多重继承,即一个类只有一个父类,为了克服单继承的缺点,Java引入了接口这一概念。接口是抽象方法定义的集合(也可以定义一些常量值),是特殊的抽象类。接口中只包含方法的定义,没有方法的实现。接口中的所有方法都是抽象的。接口中成员的作用域修饰符都是public,接口中常量值默认使用public static final 修饰。一个类可以实现多个接口,即实现了多重继承。
从书上看到上面那段话,我依然不清楚,接口到底是什么,用来干什么。为此上了知乎总结了一下:
与类相比较,类描述了一个实体,包括实体的状态,也包括实体可能发出的动作,并且描述了这个动作具体的实现步骤。而接口则只定义了一个实体可能发出的动作,没有具体的实现步骤,也没有任何状态信息。因此,接口就有点像一个规范、一个协议,是一个抽象的概念,而类实现了这个协议,满足了这个规范的具体实体,是一个具体的概念。当一组方法需要在很多类里实现,那么把它们抽象出来,做成一个接口规范。
在Java中,有些接口内部没有声明任何方法,也就是说,实现这些接口的类不需要重写任何方法,这些没有任何方法声明的接口又被叫做标识接口,标识接口对实现它的类没有任何语义上的要求,它仅仅充当一个标识的作用,用来表明实现它的类属于一个特定的类型。这个标签类似于汽车的标志图标,每当人们看到一个汽车的标志图标时,就能知道这款车的品牌。Java库中已经存在的标识接口有Cloneable 和 serializable 等。在使用时经常用instanceof 来判断实例对象的类型是否实现了一个给定的标识接口。
通过一个例子来说明标识接口的作用。例如要开发一款游戏,游戏里面有一个人物专门负责出去寻找有用的材料,假设这个人物只收集矿石和武器,而不会收集垃圾。下面通过标识接口来实现这个功能。

package cn.itcast.demo2;

public interface Stuff {

}

package cn.itcast.demo2;
//矿石
public interface Ore extends Stuff {

}

package cn.itcast.demo2;
//武器
public interface Weapon extends Stuff {

}

package cn.itcast.demo2;
//垃圾
public interface Rubbish extends Stuff {

}

package cn.itcast.demo2;
//金矿
public class Gold implements Ore {
	public String toString(){
		return "Gold";
	}
}

package cn.itcast.demo2;
//铜矿
public class Copper implements Ore {
	public String toString(){
		return "Copper";
	}
}

package cn.itcast.demo2;

public class Gun implements Weapon {
	public String toString(){
		return "Gun";
	}
}

package cn.itcast.demo2;

public class Grenade implements Weapon {
	public String toString(){
		return "Grenade";
	}
}

package cn.itcast.demo2;

public class Stone implements Rubbish {
	public String toString(){
		return "Stone";
	}
}

package cn.itcast.demo2;

import java.util.ArrayList;

public class Test {
	public static ArrayList<Stuff> collectStuff(Stuff[] s){
		ArrayList<Stuff> al = new ArrayList<Stuff>();
		for (int i = 0; i < s.length; i++) {
			//只收集矿石和武器,不收集垃圾,instanceof-是否实现了一个给定的标识接口
			if(!(s[i]instanceof Rubbish)){
				al.add(s[i]);
			}
		}
		return al;
	}
	public static void main(String[] args) {
		Stuff[] s = {new Gold(),new Copper(),new Gun(),new Grenade(),new Stone()};
		ArrayList<Stuff> al = collectStuff(s);
		System.out.println("The useful Stuff collected is:");
		for (int i = 0; i < al.size(); i++) {
			System.out.println(al.get(i));
		}
	}
}

在这里插入图片描述
在上例中,设计了三个接口:Ore 、Weapon 和 Rubbish 分别代表矿石、武器和垃圾,只要是实现Ore 或 Weapon 的类,游戏中的角色都会认为这是有用的材料,例如Gold、Copper、Gun、Grenade,因此会收集;只要是实现Rubbish 的类,都会被认为是无用的东西,例如Stone,因此不会收集。

常见笔试题:

在这里插入图片描述
java 1.8开始支持接口中定义静态方法,发了一波纠错终于得到回应,题目加了条件java8中
eclipse中测试了一下,提示的是:Illegal modifier for the interface method a; only public & abstract are permitted
你既然定义了interface(接口),那肯定是要其它的类去实现它的, 不然你定义它干吗,所以不能用protected,private 去修饰, 而要用public去修饰。
接口可以去继承一个已有的接口。

4.1.10Java的clone方法作用有哪些?

new语句返还指针引用。基本数据类型按值传递(输入参数复制),其他是按引用传递(对象的一个引用)。对象除了在函数调用时是引用传递,在使用“=”赋值也是采用引用传递。

Object类提供clone()方法,该方法返还object对象复制,该复制函数返还新对象而非引用。

使用步骤:实现clone类要继承Cloneable接口,类中重写Object类的clone()方法,在clone方法调用super.clone()。super.clone会直接或间接调用Object类的clone()方法。把浅复制的引用指向原型对象新的克隆体。

非基本类型的属性需要clone()方法才能完成深复制。

package cn.itcast.demo3;

public class Obj {
	private String str = "default value";
	
	public void setStr(String str){
		this.str = str;
	}
	
	public String toString(){
		return str;
	}
}

package cn.itcast.demo3;

public class TestRef {
	private Obj aObj = new Obj();
	private int aInt = 0;
	public Obj getAObj(){
		return aObj;
	}
	public int getAInt(){
		return aInt;
	}
	public void changeObj(Obj inObj){
		inObj.setStr("changed value");
	}
	public void changeInt(int inInt){
		inInt = 1;
	}
	public static void main(String[] args) {
		TestRef oRef = new TestRef();
		System.out.println("=======引用类型======");
		System.out.println("调用changeObj()前:" + oRef.getAObj());
		oRef.changeObj(oRef.getAObj());
		System.out.println("调用changeObj()后:" + oRef.getAObj());
		System.out.println("=======基本数据类型======");
		System.out.println("调用changeInt()前:" + oRef.getAInt());
		oRef.changeInt(oRef.getAInt());
		System.out.println("调用changeInt()后:" + oRef.getAInt());
		
		
	}
}

在这里插入图片描述
上面两个看似相似的方法有着不同的运行结果,主要原因是Java在处理基本数据类型(例如int、char、double等)时,都是采用按值传递(传递的是输入参数的复制)的方式执行,除此之外的其他类型都是按引用传递(传递的是对象的一个应用)的方式执行。

package cn.itcast.demo3;

public class Obj1 {
	private int aInt = 0;
	public int getAInt(){
		return aInt;
	}
	public void setAInt(int int1){
		aInt = int1;
	}
	public void changeInt(){
		this.aInt = 1;
	}
}

package cn.itcast.demo3;

public class TestRef1 {
	public static void main(String[] args) {
		Obj1 a = new Obj1();
		Obj1 b = a;
		b.changeInt();
		System.out.println("a:" + a.getAInt());
		System.out.println("b:" + b.getAInt());
	}
}

在这里插入图片描述

使用clone方法的步骤:

1)实现clone的类首先需要继承Cloneable接口
2)在类中重写Object类中的.clone方法
3)在clone方法中调用super.clone()
4)把浅复制的引用指向原型对象新的克隆体

package cn.itcast.demo4;

public class Obj implements Cloneable {
	private int aInt = 0;
	public int getAInt(){
		return aInt;
	}
	public void setAInt(int int1){
		aInt = int1;
	}
	public void changeInt(){
		this.aInt = 1;
	}
	public Object clone(){
		Object o = null;
		try {
			o=(Obj)super.clone();
		} catch (CloneNotSupportedException e) {			
			e.printStackTrace();
		}
		return o;
	}
}

package cn.itcast.demo4;

public class TestRef {

	public static void main(String[] args) {
		Obj a = new Obj();
		Obj b = (Obj)a.clone();
		b.changeInt();
		System.out.println("a:" + a.getAInt());
		System.out.println("b:" + b.getAInt());
	}

}

在这里插入图片描述

当类中只有一些基本的数据类型时,采用上述方法就可以;

当类中包含一些对象时,就需要用到深复制;
实现方法是在对对象调用clone()方法完成复制之后,接着对对象中的非基本类型的属性也调用clone方法完成深复制。

package cn.itcast.demo5;

import java.util.Date;

public class Obj implements Cloneable {
	private Date birth = new Date();
	public Date getBirth(){
		return birth;
	}
	public void setBirth(Date birth){
		this.birth = birth;
	}
	public void changeDate(){
		this.birth.setMonth(4);
	}
	public Object clone(){
		Obj o = null;
		try {
			o=(Obj)super.clone();
		} catch (CloneNotSupportedException e) {			
			e.printStackTrace();
		}
		//实现深复制
		o.birth = (Date)this.getBirth().clone();
		return o;
	}
}

package cn.itcast.demo5;

public class TestRef {

	public static void main(String[] args) {
		Obj a = new Obj();
		Obj b = (Obj)a.clone();
		b.changeDate();
		System.out.println("a:" + a.getBirth());
		System.out.println("b:" + b.getBirth());
	}

}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_40807247/article/details/86240366