面试准备系列——Java基础技术篇(1)/基本概念

1.Java语言有哪些优点

1.Java是纯面向对象的语言
2.平台无关性。Java语言可以“一次编译,到处运行”。无论是什么平台都能够对Java语言进行编译,编译之后的程序在Java虚拟机(JVM)上运行。
3.Java提供了很多的内置的类库,可以简化开发工作。例如提供了多线程的支持,对网络的支持,最重要的是提供了垃圾回收器。
4.提供了对Web应用开发的支持。
5.具有较好的安全性和健壮性。
6.去除了C++语言中难以理解、容易混淆的特性,例如头文件、指针、结构运算符重载、多重继承等,使得程序更加严谨、简洁。

2.Java与C/C++有何异同

  • 相同点:都是面向对象的语言,都使用了面向对象的思想(如继承、封装、多态),由于面向对象有许多非常好的特性,因此两者都有很好的重用性。
  • 不同点:
    1)Java为解释性语言,而C/C++为编译性的语言。Java的执行速度比C++的慢,但是能够跨平台运行,而C/C++则不能。
    2)Java为纯面向对象语言,所有的代码(函数、变量)都必须在类中实现,而C/C++则具备面向过程和面向对象的特点。
    3)与C/C++语言相比,Java语言中没有指针的概念,这有效防止了C/C++中操作指针引起的问题,使系统更安全。
    4)与C/C++语言相比,Java语言中不支持多重继承,但是Java引入了接口的概念,允许一个类继承多个接口。
    5)Java中支持垃圾的自动回收,而C/C++中需要开发人员去管理。
    6)Java具有平台无关性,例如int类型在Java中总是占据32个字节,但是C中会因平台不同而不一样。
    常见的题目:
    在这里插入图片描述
    注意C选项中,只有静态方法才属于类中的成员,非静态方法不是,它属于实例成员。静态成员得是在当前类的外部,使用类名.方法或者类名.成员变量能够直接使用的才是。

3.为什么需要public static void main(String[] args)这个方法

public static void main(String[] args)为Java程序的入口方法,JVM在运行程序的时候,会首先查找main方法。其中,public是权限修饰符,表明任何类或对象都可以访问这个方法,static表明main方法是一个静态方法,即方法中的代码是存储在静态存储区的,只要类被加载后,就可以使用该方法而不需要通过实例化对象来访问,可以直接通过类名.main()直接访问,JVM在启动的时候就是按照上述方法的签名(必须有public与static修饰,返回值为void,且方法的参数为字符串数组)来查找方法的入口地址,如果能找到就执行,找不到则会报错。void表明方法没有返回值,main是JVM识别的特殊方法名,是程序的入口方法。字符串数组参数args为开发人员在命令行状态下与程序交互提供了一种手段。

因为main为程序的入口方法,因此当程序运行的时候,第一个执行的方法就是main方法。通常来讲,要执行一个类的方法,首先必须实例化一个类的对象,然后通过对象来调用这个方法。但是由于main是程序的入口方法,此时还没有实例化对象,因此在编写main方法的时候就要求不需要实例化对象就可以调用这个方法,鉴于此,main方法需要被定义成public与static。

  • 引申:

    • (1)main方法是否还有其它可用的定义格式?

1)由于public与static没有先后顺序关系,由此下面的定义也是合理的:

   static public void main(String[] args)

2)也可以把main方法定义为final

    public static final void main(String[]args)

3)也可以用synchronized来修饰main方法:

   static public synchronized voidmain(String[] args)

不管哪种定义方式,都必须保证main方法的返回值为void,有static与public关键字修饰。同时由于main方法为程序的入口方法,因此不能用abstract关键字来修饰。

  • (2)同一个.java文件中是否可以有多个main方法?

虽然每个类中都可以定义main方法,但是只有与文件名相同的用public修饰的类中的main方法才能作为整个程序的入口方法。如下例所示,创建了一个名为Test.java的文件。
在这里插入图片描述
常见笔试题:
在这里插入图片描述

4.如何实现在main()方法执行前输出“Hello World”

众所周知,在 Java语言中,main()方法是程序的入口方法,在程序运行时,最先加载的就是main()方法,但这是否意味着main()方法就是程序运行时第一个被执行的模块呢?
答案是否定的。在Jawa语言中,由于静态块在类被加载时就会被调用,因此可以在main()方法执行前,利用静态块实现输出“ Hello World”的功能,以如下代码为例。

public class OutPutBeforeMain {

    static {
        System.out.println("hello world!   ---> before main");
    }

    public static void main(String[] args) {
        System.out.println("hello world!   ---> in main");
    }

}

输出:
在这里插入图片描述
由于静态代码块不管顺序如何,都会在main方法之前执行,因此下边的代码执行的结果和上边的是一样的:

public class OutPutBeforeMain {

    public static void main(String[] args) {
        System.out.println("hello world!   ---> in main");
    }

    static {
        System.out.println("hello world!   ---> before main");
    }

}

输出:
在这里插入图片描述

5.Java程序的初始化顺序是怎样的

在Java语言中,当实例化对象时,对象所在类的所有成员变量首先要进行初始化,只有当所有类成员完成初始化后,才会调用对象所在类的构造函数创建对象。
Java程序的初始化-般遵循3个原则( 优先级依次递减):
①静态对象(变量)优先于非静态对象(变量)初始化,其中,静态对象(变量)只初始化-一次,而非静态对象(变量)可能会初始化多次。
②父类优先于子类进行初始化。
③按照成员变量的定义顺序进行初始化。

即使变量定义散布于方法定义之中,它们依然在任何方法(包括构造函数)被调用之前先初始化。

Java程序初始化工作可以在许多不同的代码块中来完成(例如静态代码块、构造函数等),它们执行的顺序如下:
父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量、父类非静态代码块、父类构造函数、子类非静态变量、子类非静态代码块、子类构造函数。
下面给出一个不同模块初始化时执行顺序的一个例子。

public class InitializeOrder {

    public static void main(String[] args) {
        //初始化顺序:父类静态变量->父类静态代码块->子类静态变量->子类静态代码块->父类非静态变量->父类非静态代码块->
        // 父类构造函数->子类非静态变量->子类非静态代码块->子类构造函数
        new Sun();
    }

}


class Father {
    static {
        System.out.println("father static block");
    }

    {
        System.out.println("father block");
    }

    public Father(){
        System.out.println("father constructor");
    }
}

class Sun extends Father {
    static {
        System.out.println("sun static block");
    }

    {
        System.out.println("sun block");
    }

    public Sun() {
        System.out.println("sun constructor");
    }
}

输出:
在这里插入图片描述

6.Java中的作用域都有哪些?

Java语言中,作用域是由花括号的位置决定的,他决定了其定义的变量名的可见性与生命周期。
在Java语言中,变量的类型主要有3种:成员变量、静态变量和局部变量

  • 静态变量:静态变量不依赖于特定的实例,而是被所有实例共享,也就是说,只要一个类被加载,JVM就会给类的静态变量分配存储空间。因此可以通过类名.变量名来访问静态变量
  • 局部变量:局部变量的作用域与可见性为它所在的花括号内
  • 成员变量:类的成员变量的作用范围同类的实例化对象的作用范围相同。当类被实例化的时候,成员变量就会在内存中分配空间,并初始化。直到类的实例化对象的生命周期结束时,成员变量的生命周期才结束。

此外成员变量也有4中作用域,区别如下:
在这里插入图片描述
重点说一下protected和default:

protected:表名成员变量或方法对该类自身,与它在同一个包中的其他类,在其他包中的该类的子类都可见

defaul:表明该成员变量或方法只有自己和与其位于同一包内的类可见。

若父类与子类处于同一包内,则子类对父类的default成员变量或方法都有访问权限;若父类与子类处于不同的package内,则没有访问权限。

需要注意:
这些修饰符只能修饰成员变量,不能用来修饰局部变量。private与protected不能用来修饰类(只有public、abstract或final能用来修饰类)。

常见的笔试题:
在这里插入图片描述
答案:D。实例方法:创建实例之后,可以用实例.方法名 调用的,类方法可以直接 类名.方法名 来调用。
当超类(其他类)的实例方法或者类方法为private的时候,是不能被子类调用的。

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

一个Java文件汇总可以定义多个类,但是最多只能有一个类可以被public修饰的,并且这个类的类名必须和文件名相同。
若这个文件中没有public的类,则文件名随便是一个类的名字即可。需要注意的是,当用javac指令编译这个.java文件的时候,他会给每一个类生成一个对应的.class文件。示例如下:

class Father {
	public void print() {
		System.out.println("Father");
	}
}

public class Sun extends Father {
	public static void main() {
		Father f = new Sun();
		f.print();
	}
}

使用javac Sun.java指令编译上述的代码,会生成两个字节码的文件:Father.class和Sun.class,然后使用java Sun指令执行代码,此时控制台输出的结果是:Father 。

8.什么是构造函数

构造函数是一种特殊的函数,用来在对象实例化时初始化对象的成员变量。在Java语言中,构造函数具有以下特点:

  • 1.构造函数必须与类的名字相同,并且不能有返回值(返回值也不能为void)。
  • 2.每个类可以有多个构造函数。当开发人员没有提供构造函数时,编译器在把源代码编译成字节码的过程中会提供一个没有参数默认的构造函数,但该构造函数不会执行任何代码。如果开发人员提供了构造函数,那么编译器就不会在创建默认的构造函数了。
  • 3.构造函数可以有0个、1个或1个以上的参数。
  • 4.构造函数总是伴随着new操作一起调用,且不能由程序的编写者直接调用,必须要由系统调用。构造函数在对象实例化时会被自动调用,且只运行一次;而普通的方法是在程序执行到它时被调用,且可以被对象调用多次。
  • 5.构造函数的主要作用是完成对象的初始化工作。
  • 6.构造函数不能被继承,因此,它不能被覆盖,但是构造函数能够被重载,可以使用不同的参数个数或参数类型来定义多个构造函数。
  • 7.子类可以通过super关键字来显式地调用父类的构造函数,当父类没有提供无参数的构造函数时,子类的构造函数中必须显式地调用父类的构造函数。如果父类提供了无参数的构造函数,此时子类的构造函数就可以不显式地调用父类的构造函数,在这种情况下编译器会默认调用父类提供的无参数的构造函数。当有父类时,在实例化对象时会先执行父类的构造函数,然后执行子类的构造函数。
  • 8.当父类和子类都没有定义构造函数时,编译器会为父类生成一个默认的无参数的构造函数,给子类也生成一个默认的无参数的构造函数。此外,默认构造器的修饰符只跟当前类的修饰符有关(例如,如果一个类被定义为public,那么它的构造函数也是public)。
public class Test{
	public Test(){
		System.out.println("construct");
	}
	public void Test(){
		System.out.println("call Test");
	}
	public static void main(String[] args){
		Test a = new Test();//调用构造函数
		a.Test();			//调用Test方法
	}
}

程序运行结果为:
construct
call Test

常见笔试题:
在这里插入图片描述

9.为什么Java中有些接口没有任何方法

由于Java不支持多重继承,即一个类只有一个父类,为了克服单继承的缺点,Java引入了接口这一概念。接口是抽象方法定义的集合(也可以定义一些常量值),是特殊的抽象类。接口中只包含方法的定义,没有方法的实现。接口中的所有方法都是抽象的。接口中成员的作用域修饰符都是public,接口中常量值默认使用public static final 修饰。一个类可以实现多个接口,即实现了多重继承。

与类相比较,类描述了一个实体,包括实体的状态,也包括实体可能发出的动作,并且描述了这个动作具体的实现步骤。而接口则只定义了一个实体可能发出的动作,没有具体的实现步骤,也没有任何状态信息。因此,接口就有点像一个规范、一个协议,是一个抽象的概念,而类实现了这个协议,满足了这个规范的具体实体,是一个具体的概念。当一组方法需要在很多类里实现,那么把它们抽象出来,做成一个接口规范。
在Java中,有些接口内部没有声明任何方法,也就是说,实现这些接口的类不需要重写任何方法,这些没有任何方法声明的接口又被叫做标识接口,标识接口对实现它的类没有任何语义上的要求,它仅仅充当一个标识的作用,用来表明实现它的类属于一个特定的类型。这个标签类似于汽车的标志图标,每当人们看到一个汽车的标志图标时,就能知道这款车的品牌。Java库中已经存在的标识接口有Cloneable 和 serializable 等。在使用时经常用instanceof 来判断实例对象的类型是否实现了一个给定的标识接口。

   常见笔试题:

在这里插入图片描述
答案:ACD

10.(重点)Java中的clone方法有什么作用(对象与引用的区别、深浅复制)

由于指针不仅会给开发人员带来使用上的不便,而且也是造成程序不稳定的根源,为了消除C/C++语言的这些缺点,Java语言取消了指针的概念,但这只是在Java语言中没有明确提供指针的概念和用法,而实质上每个new语句返回的都是一个指针的引用,只不过在大部分情况下开发人员不需要关心如何去操作这个指针而已。

由于Java取消了指针的概念,因此开发人员在编程过程中往往忽略了对象和引用的区别,示例如下:

class Obj{
	public void setStr(String str){
		this.str = str;
	}
	private String str = "default value";
	public String toString(){
		return str;
	}
}
public class TestRef{
	private Obj aObj = new Obj();
	private int aInt = 0;
	public Obj get AObj(){
		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());
	}
}

程序运行结果为:
***************引用类型***************
调用changeObj()前:default value
调用changeObj()后:changed value
***************基本数据类型***************
调用changeInt()前:0
调用changeInt()后:0

上面两个看似类似的方法却有着不同的运行结果,主要原因是Java在处理基本数据类型(例如int、char、double等)时,都是采用按值传递(传递的是输入参数的复制)的方式执行,除此之外的其他类型都是按引用传递(传递的是对象的一个引用)的方式执行。对象除了在函数调用时是引用传递,在使用“=”赋值时也采用引用传递,示例代码如下:

class Obj{
	private int aInt = 0;
	public int getAInt(){
		return aInt;
	}
	public void setAInt(int int1){
		aInt = int1;
	}
	public void changeInt(){
		this.aInt = 1;
	}
}
public class TestRef{
	public static void main(String[] args){
		Obj a = new Obj();
		Obj b = a;
		b.changeInt();
		System.out.println("a:" + a.getAInt());
		System.out.println("b:" + b.getAInt());
	}
}

程序运行结果为:
a:1
b:1

实际编程中,经常会遇到从某个已有的对象A创建出另外一个与A具有相同状态的对象B,并且对B的修改不会影响到A的状态,例如,Prototype(原型)模式中,就需要clone一个对象实例。在Java语言中,仅仅通过简单的赋值操作显然无法达到这个目的,而Java提供了一个简单有效的clone()方法来满足这个需求。

Java中的所有类默认都继承自Object类,而Object类中提供了一个clone()方法。这个方法的作用是返回一个Object对象的复制。这个复制函数返回的是一个新的对象而不是一个引用。以下是使用clone()方法的步骤。

1.实现clone的类首先需要继承Cloneable接口。Cloneable接口实质上是一个标识接口,没有任何接口方法。
2.在类中重写Object类中的clone()方法。
3.在clone方法中调用super.clone()。无论clone类的继承结构是什么,super.clone()都会直接或间接调用java.lang,Object类的clone()方法。
4.把浅复制的引用指向原型对象新的克隆体。

对上面的例子引入clone方法如下:

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(CloneNotSupportException e){
			e.printStackTrace();
		}
		return o;
	}
}
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());
	}
}

程序运行结果为:
a:0
b:1

在C++语言中,当开发人员自定义复制构造函数时,会存在浅复制与深复制之分。Java语言在重载clone()方法时也存在同样的问题,当类中只有一些基本的数据类型时,采用上述方法就可以了,但是当类中包含了一些对象时,就需要用到深复制了,实现方法是在对对象调用clone()方法完成复制后,接着对对象中的非基本类型的属性也调用clone()方法完成深复制,示例如下:

import java.util.Date;
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;
	}
}
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());
	}
}

输出:
在这里插入图片描述
那么在编程时,如何选择使用哪种复制方式呢?首先,检查类有无非基本类型(即对象)的数据成员。若没有,则返回super.clone()即可;若有,确保类中包含的所有的非基本类型的成员变量都实现了深复制。

Object o = super.clone();//先执行浅复制

对每一个对象attr执行以下语句:

o.attr = this.getAttr().clone();

最后返回o。

需要注意的是,clone()方法的保护机制在Object中clone()是被声明为protected的。以User类为例,通过声明为protected,就可以保证只有User类里面才能“克隆”User对象。

一个概念:深复制和浅复制的区别联系:
在这里插入图片描述

11.说说Java中的反射机制

答: 反射机制是指在运行中,对于任意一个类,都能够知道这个类的所有属性和方法。对于任意一个对象,都能够调用它的任意一个方法和属性。即动态获取信息和动态调用对象方法的功能称为反射机制。

解析:

反射机制是Java中的高级特性之一,市场上流行的各个框架,比如Spring等底层都依赖于Java中的反射机制。那么,我们一起来详细看下反射机制吧。

反射机制的作用:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法,生成动态代(dai)理

与反射相关的类:
Class:表示类,用于获取类的相关信息
Field:表示成员变量,用于获取实例变量和静态变量等
Method:表示方法,用于获取类中的方法参数和方法类型等
Constructor:表示构造器,用于获取构造器的相关参数和类型等

这里我们讲述下如何获取Class类吧,获取Class类有三种基本方式:

  • (1)通过类名称.class来获取Class类对象:
Class c = int.class;
Class c = int[ ].class;
Class c = String.class
  • (2)通过对象.getClass( )方法来获取Class类对象:
Class c = obj.getClass( );
  • (3)通过类名称加载类Class.forName( ),只要有类名称就可以得到Class:
Class c = Class.forName(“cn.ywq.Demo”);

常见的笔试题:
Java中创建对象的方式有几种?
答案:4种:
1.通过new关键字创建
2.通过反射机制创建
3.通过clone()方法创建
4.通过反序列化的方式创建

12.package有什么作用

1). 提供多层命名空间,解决命名冲突,通过使用package,使得处于不同package中的类可以存在相同的名字。
2). 对类按功能进行分类,使项目的组织更加清晰。
常见笔试题:
在这里插入图片描述

13.如何实现类似于C语言中的函数指针功能

  • 1.什么是函数指针

如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配了一段存储空间,这段存储空间的起始地址(有称入口地址)称为这个函数的指针。可以定义一个指向函数的指针变量,用来存放某一个函数的起始地址,这个指针变量指向这个函数。开发人员在使用时根据自己写需求传递自定义的函数来定义的函数来实现指定的功能。

  • 2.如何实现类似于C语言中函数指针的功能
    在Java语言中没有指针的概念,可以利用接口与类实现同样的效果,应先定义一个接口,然后在接口中声明要调用的方法,接着实现这个接口(不同的功能实现,例如一个升序排列,一个降序排序),最后把这个实现类的一个对象作为参数给调用程序,调动程序通过这个参数来调用指定的函数。

具体实现如下:

interface IntCompare
{
	public int cmp(int a,int b);
}
 
class Cmp1 implements IntCompare
{
	public int cmp(int a,int b)
	{
		if(a>b)
			return 1;
		else if(a<b)
			return -1;
		else 
			return 0;
	}
}
 
class Cmp2 implements IntCompare
{
	public int cmp(int a,int b)
	{
		if(a>b)
			return -1;
		else if(a<b)
			return 1;
		else
			return 0;
	}
}
 
class HelloWorld
{
	public static void main(String[] args)
	{
		int[] array1 = {7,3,19,40,4,7,1};
		insertSort(array1,new Cmp1());
		System.out.println("升序排列");
		for(int i=0;i<array1.length;i++)
		{
			System.out.print(array1[i]+" ");
		}
	
		System.out.println();
		int[] array2 = {7,3,19,40,4,7,1};
		insertSort(array2,new Cmp2());
		System.out.println("降序排列");
		for(int i =0;i<array2.length;i++)
		{
			System.out.print(array2[i]+" ");
		}
		
	}
									
	public static void insertSort(int[] a,IntCompare cmp)	
	{
		if(a!=null)
		{
			
			for(int i=1;i<a.length;i++)
			{
				int temp = a[i],j=i;
				if(cmp.cmp(a[j-1], temp)==1)    
				{
					while(j>=1&&cmp.cmp(a[j-1],temp)==1)	
					{
						a[j] = a[j-1];
						j--;
					}
				}
				a[j] = temp;
			}
			for(int i=1;i<a.length;i++)
			{
				int temp = a[i];
				int j = i-1;
				while(j>=0&&cmp.cmp(a[j], temp)==1)
				{
					a[j+1] = a[j];
					j--;
				}
				a[j+1] = temp;
			}
		}
	}
}

13.Java中的元注解都有哪些

Java中提供了4个元注解,元注解的作用是负责注解其它注解。

  • 1.@Target:
    说明注解所修饰的对象范围,关键源码如下:
public @interface Target { 
    ElementType[] value(); 
} 
public enum ElementType { 
  TYPE,FIELD,METHOD,PARAMETED,CONSTRUCTOR,LOCAL_VARIABLE,ANNOCATION_TYPE,PACKAGE,TYPE_PARAMETER,TYPE_USE 
} 

例如,如下的注解使用@Target标注,表明MyAnn注解就只能作用在类/接口和方法上。

@Target({ElementType.TYPE, ElementType.METHOD}) 
public @interface MyAnn { 
}
  • 2.@Retention:(保留策略)
    保留策略定义了该注解被保留的时间长短。关键源码如下:
public @interface Retention { 
    RetentionPolicy value(); 
} 
public enum RetentionPolicy { 
    SOURCE, CLASS, RUNTIME 
}

其中,SOURCE:表示在源文件中有效(即源文件保留);CLASS:表示在class文件中有效(即class保留);RUNTIME:表示在运行时有效(即运行时保留)。例如,@Retention(RetentionPolicy.RUNTIME)标注表示该注解在运行时有效

  • 3.@Documented:
    该注解用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被javadoc此类的工具文档化。Documented是一个标记注解,没有成员。关键源码如下:
public @interface Documented {
}
  • 4.@Inherited:
    该注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。关键源码如下:
public @interface Inherited {
}

13.1 注解的作用与定义

作用:代替繁杂的配置文件,简化开发。
定义:定义注解类不能使用class、enum以及interface,必须使用@interface。下边是一个简单的注解定义:

public @interface MyAnn{} 

定义注解的属性:

public @interface MyAnn { 
    String value(); 
    int value1(); 
}
// 使用注解MyAnn,可以设置属性
@MyAnn(value1=100,value="hello") 
public class MyClass { 
}

定义注解时候的value就是属性,看着是一个方法,但我们称它为属性。当为注解指定属性后,那么在使用注解时就必须要给属性赋值了。

14.JDK,JRE和JVM的区别与联系有哪些?

三者的基本概念可以概括如下:

  • JDK(Java Development Kit)是一个开发工具包,是Java开发环境的核心组件,并且提供编译、调试和运行一个Java程序所需要的所有工具,可执行文件和二进制文件,是一个平台特定的软件
  • JRE(Java Runtime Environment)是指Java运行时环境,是JVM的实现,提供了运行Java程序的平台。JRE包含了JVM,但是不包含Java编译器/调试器之类的开发工具
  • JVM(Java Virtual Machine)是指Java虚拟机,当我们运行一个程序时,JVM负责将字节码转换为特定机器代码,JVM提供了内存管理/垃圾回收和安全机制等

区别与联系:

  • JDK是开发工具包,用来开发Java程序,而JRE是Java的运行时环境
  • JDK和JRE中都包含了JVM
  • JVM是Java编程的核心,独立于硬件和操作系统,具有平台无关性,而这也是Java程序可以一次编写,多处执行的原因

本文章是一个学习笔记,主要借鉴于《Java程序员面试笔试宝典》。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值