java基础知识-基本概念

1.1 java语言有哪些优点?

1.java语言为纯面向对象的语言。

2.平台无关性。java语言的优点便是“一次编译,到处执行”。编译后的程序不会被平台所约束,因此java语言有很好的移植性。

3.java提供了许多内置的类库,将代码封装好来给操作人员使用,从而大大减少开发人员的时间。

4.提供了对web应用的开发的支持。

5.具有较好的安全性和健壮性。

6.去除了c++语言中难以理解,容易混淆的特性,例如头文件,指针,结构,单元运算符重载,虚拟基础类,多重继承等,所以java语言是由c++语言改进并重新设计而来的

1.2 java语言和c/c++有什么异同。

java和C++都是面向对象的语言,都使用了面向对象的思想(例如封装,继承,多态),由于面向对象有许多非常好的特性(继承,组合等),因此二者都有很好的重用性。

下面重点说一下不同点:

1.java为解释型语言,c/c++为编译型语言,java代码由java编译器编译成字节码,然后由JVM解释,C语言代码经过编译和链接生成可执行的二进制代码,因此java的执行速度比c++慢,但是java可跨平台执行,c/c++不能

2.java语言没有指针

3.java只能实现单重继承,但是可以引入多个接口

4.java为纯面向对象语言,所有代码必须在类里实现

5.java语言提供了垃圾回收器来实现对垃圾的自动回收,c++语言中需要开发人员去管理对内存的分配。C语言,通常会把释放资源的代码放在析构函数中,Java没有但是有finalize()方法。

java语言不支持运算符重载,C语言支持

java不支持默认函数参数,c语言支持

java不提供goto语句,c/c++支持,但是在java中goto为保留关键字

java不支持自动强制类型装换,c语言支持

java具有平台无关性,就是对每种数据类型分配固定长度。

java提供对注释文档的内建支持

java包含了一些标准库

1.3 为什么使用public static void main(String[] args)方法?

main是程序的入口方法,所以程序执行时第一个执行的方法就是main方法。

main()方法定义的其他几种格式:

1.static pubic void main(String[] args)

static 和public无先后顺序

2.public static final void main(String[] args)

可以定义为final

3.static public synchronized void main(String[] args)

可以定义为synchronized

不管哪种定义方式,必须保证main()方法类型为void并且有static和public关键字修饰。不可以用abstract关键字,因为main()为程序的入口方法。

1.4静态块

静态块会在类被加载时调用,可以在main()方法前执行

例如:

public class jingtaikuai {

1
2
3
4
5
6
public static void main(String[] args) {
System.out.println("hello word");
}
static{
System.out.println("静态块");
}

}

执行结果:

静态块
hello word

1.5 java程序初始化顺序是怎样的

java程序的初始化一般遵循三个原则(优先级依次递减):

1.静态对象优先于非静态对象

2.父类优先于子类

3.按照成员变量定义顺序进行初始化

常见面试题:

下面代码的运行结果是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class B extends Object{
static {
System.out.println("load b1");
}
public B(){
System.out.println("create b");
}
static{
System.out.println("load b2");
}
}
class A extends B{
static {
System.out.println("load a");
}
public A(){
System.out.println("create a");
}
}
public class 初始化顺序 {
public static void main(String[] args) {
new A();
}

}

执行结果:

load b1
load b2
load a
create b
create a

1.6 java作用域

在Java语言中,变量的类型主要有3种:成员变量、静态变量和局部变量

首先说静态变量跟局部变量

静态变量不依赖于特定的实例,而是被所有实例共享,也就是说,只要一个类被加载,JVM就会给类的静态变量分配

存储空间。因此可以通过类名.变量名来访问静态变量

局部变量的作用域与可见性为它所在的花括号内

类的成员变量的作用范围同类的实例化对象的作用范围相同。当类被实例化的时候,成员变量就会在内存中分配空间,并初始化。

直到类的实例化对象的生命周期结束时,成员变量的生命周期才结束。

作用域与可见性 当前类 同一package 子类 其他package
public
private × × ×
protected ×
default × ×

重点说一下protected和default:

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

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

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


还有需要注意的是,这些修饰符只能修饰成员变量,不能修饰局部变量。

private和protected不能用来修饰类

1.7 一个java文件中能否定义多个类

一个java文件中可以定义多个类,但是最多只能有一个类被public修饰,并且这个类的类名必须和文件名相同。

1.8 java的构造函数

一、什么是构造函数

java构造函数,也叫构造方法,是java中一种特殊的函数。函数名与相同,无返回值。

作用:一般用来初始化成员属性和成员方法的,即new对象产生后,就调用了对象了属性和方法。

在现实生活中,很多事物一出现,就天生具有某些属性和行为。比如人一出生,就有年龄、身高、体重、就会哭;汽车一出产,就有颜色、有外观、可以运行等。这些,我们就可以将这些天然的属性和行为定义在构造函数中,当new实例化对象时,也就具有这些属性和方法了,没必要再去重新定义了,从而加快了编程效率。

构造函数是对象一建立就运行,给对象初始化,就包括属性,执行方法中的语句。

而一般函数是对象调用才执行,用”.方法名“的方式,给对象添加功能。

一个对象建立,构造函数只运行一次。

而一般函数可以被该对象调用多次。

二、构造函数的特点

1、函数名与类名相同

2、不用定义返回值类型。(不同于void类型返回值,void是没有具体返回值类型;构造函数是连类型都没有)

3、不可以写return语句。(返回值类型都没有,也就不需要return语句了

注:一般函数不能调用构造函数,只有构造函数才能调用构造函数。

三、示例

1、无参构造函数中只定义了一个方法。new对象时,就调用与之对应的构造函数,执行这个方法。不必写“.方法名”。

1
2
3
4
5
6
7
8
9
10
11
12
package javastudy;

public class ConfunDemo {
public static void main(String[] args) {
Confun c1=new Confun(); //输出Hello World。new对象一建立,就会调用对应的构造函数Confun(),并执行其中的println语句。
}
}
class Confun{
Confun(){ //定义构造函数,输出Hello World
System.out.println("Hellow World");
}
}

输出:Hellow World

2、有参构造函数,在new对象时,将实参值传给private变量,相当于完成setter功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package javastudy;

public class ConfunDemo3 {
public static void main(String[] args){
Person z=new Person("zhangsan",3); //实例化对象时,new Person()里直接调用Person构造函数并转转实参,相当于setter功能
z.show();
}
}

class Person{
private String name;
private int age;
public Person(String n,int m){ //有参数构造函数,实现给private成员变量传参数值的功能
name=n;
age=m;
}
//getter //实例化对象时,完成了sett功能后,需要getter,获取实参值。
public String getName(){
return name;
}
public int getAget(){
return age;
}
public void show(){ //获取private值后,并打印输出
System.out.println(name+"\n"+age);
}
}

输出:
zhangsan
3

以上代码,我们也可以将show()方法中的输出语句直接放在构造函数中,new对象时,即可直接输出值,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package javastudy;

public class ConfunDemo3 {
public static void main(String[] args){
Person z=new Person("zhangsan",3); //实例化对象时,new Person()里直接调用Person构造函数并转转实参,同时执行输出语句
}
}

class Person{
private String name;
private int age;
public Person(String n,int m){ //有参数构造函数,实现给private成员变量传参数值的功能,同时直接输出值
name=n;
age=m;
System.out.println(name+"\n"+age);
}
}

输出:
zhangsan
3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class ConFun
{
public static void main(String[] args){
Person z=new Person(20,"zhangsan");
System.out.println(z.getAge()+z.getName());
}
}

class Person
{
private int age;
private String name;
public Person(int x,String y){
age=x;
name=y;
}
public int getAge(){
return age;
}
public String getName(){

return name;
}
}

3、一个对象建立后,构造函数只运行一次

如果想给对象的值再赋新的值,就要使用set和get方法,此时是当做一般函数使用

如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package javastudy;

public class ConfunDemo4 {
public static void main(String[] args) {
PersonDemo s=new PersonDemo("李三",33); //new对象时,即调用对应的构造函数,并传值。同时,不能new同一个对象多次,否则会报错。
s.setName("李五"); //对象建立后,想变更值时,就要用set/get方法,重新设置新的值
s.setName("阿尔法狗"); //并可调用对象多次。
s.print();
}
}
class PersonDemo{
private String name;
private int age;
PersonDemo(String n,int m){ //建立有参构造函数,用于给两个private变量name、age赋值,同时输出值
name=n;
age=m;
System.out.println("姓名:"+name+"年龄:"+age);
}
public void setName(String x){ //set方法,用于再次给name赋值
name=x;
}
public String getName(){ //get方法,用于获取name的赋值
return name;
}
public void print(){
System.out.println(name);
}
}

输出结果:

姓名:李三年龄:33
阿尔法狗

四、默认构造函数

当一个类中没有定义构造函数时,系统会给该类中加一个默认的空参数的构造函数,方便该类初始化。只是该空构造函数是隐藏不见的。

如下,Person(){}这个默认构造函数是隐藏不显示的。

1
2
3
4
class Person
{
//Person(){}
}

当在该类中自定义了构造函数,默认构造函数就没有了。

如果仍要构造函数,则需要自己在类中手动添加。

五、构造函数的重载

构造函数也是函数的一种,同样具备函数的重载(Overloding)特性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Person
{
private String name;
private int age;

Person()
{
System.out.println("A:name="+name+":::age="+age);
}

Person(String n)
{
name = n;
System.out.println("B:name="+name+":::age="+age);
}

Person(String n,int a)
{
name=n;
age=a;
System.out.println("C:name="+name+":::age="+age);
}

}

class PersonDemo2
{
public static void main(String[] args)
{
Person p1=new Person();
Person p2=new Person("lishi");
Person p3=new Person("lishi",10);
}
}

输出结果:
A:name=null:::age=0
B:name=lishi:::age=0
C:name=lishi:::age=10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Person
{
private String name;
private int age;

Person()
{
System.out.println("A:name="+name+":::age="+age);
cry();
}

Person(String n)
{
name = n;
System.out.println("B:name="+name+":::age="+age);
cry();
}

Person(String n,int a)
{
name=n;
age=a;
System.out.println("C:name="+name+":::age="+age);
cry();

}
void cry()
{
System.out.println("Cry...............");
}

}

class PersonDemo2
{
public static void main(String[] args)
{
Person p1=new Person();
Person p2=new Person("lishi");
Person p3=new Person("lishi",10);
}
}

输出结果:
A:name=null:::age=0
Cry……………
B:name=lishi:::age=0
Cry……………
C:name=lishi:::age=10
Cry……………

转自:http://www.cnblogs.com/ibelieve618/p/6364541.html

1.9 java中的clone方法

java中所有的类都继承自Object类,这个类提供了一个clone的方法,这个方法的作用是返回一个Object对象的复制。

使用步骤:

1.继承Cloneable 接口

2.重写clone()方法

3.clone方法中调用super.clone()

4.把浅复制的引用指向原型对象新的克隆体


一、简单用法

只需要在需要clone的对象上实现(implements)Cloneable接口,然后再在类中加上clone方法,在方法中只需要调用super.clone(),根据自己的需要实现即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Student implements Cloneable {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}

@Override
protected Student clone() throws CloneNotSupportedException {
return (Student)super.clone();
}

public static void main(String[] args) {
Student stu = new Student();
stu.setAge(1);
stu.setName("aa");
System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName());
try {
Student sC = stu.clone();
System.out.println(sC + " sC.age: " + sC.getAge() + " sC.name: " + sC.getName());
sC.setAge(12);
sC.setName("bb");
System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName());
System.out.println(sC + " sC.age: " + sC.getAge() + " sC.name: " + sC.getName());
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

输出结果:

testClone.Student@15db9742 age: 1 name: aa
testClone.Student@6d06d69c sC.age: 1 sC.name: aa
testClone.Student@15db9742 age: 1 name: aa
testClone.Student@6d06d69c sC.age: 12 sC.name: bb

分析结果:1、根据输出结果中前边的类名,可以得出被克隆对象的与原来的对象是同一种类型。2、根据内存地址(hashcode)知道,被克隆对象的与原来的对象是存在于内存中的不同的两个对象。所以后边有一个赋值,对原来对象没有任何影响。

二、“影子”克隆与深度克隆

首先看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class Bag{//学生的书包
private int width;
private String logo;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public String getLogo() {
return logo;
}
public void setLogo(String logo) {
this.logo = logo;
}
}

public class Student2 implements Cloneable {
private String name;
private int age;
private Bag bag;

public Bag getBag() {
return bag;
}
public void setBag(Bag bag) {
this.bag = bag;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}

@Override
protected Student2 clone() throws CloneNotSupportedException {
return (Student2)super.clone();
}

public static void main(String[] args) {
Student2 stu = new Student2();
stu.setAge(1);
stu.setName("aa");
Bag b = new Bag();
b.setWidth(10);
b.setLogo("Nike");
stu.setBag(b);
printStudent(stu);
try {
Student2 sC = stu.clone();
printStudent(sC);
sC.setAge(12);
sC.setName("bb");
sC.getBag().setWidth(100);//改变书包的属性
sC.getBag().setLogo("JNike");
printStudent(stu);
printStudent(sC);
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**
* 输出
* @param stu
*/
private static void printStudent(Student2 stu) {
System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName() +
" bag: " + stu.getBag() + "(" + stu.getBag().getLogo() + " width: " +
stu.getBag().getWidth() + ")");
}

}
输出结果:
testClone.Student2@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike width: 10)
testClone.Student2@7852e922 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike width: 10)
testClone.Student2@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(JNike width: 100)
testClone.Student2@7852e922 age: 12 name: bb bag: testClone.Bag@6d06d69c(JNike width: 100)

分析:发现是不是跟预期的不太一样,通过第二个同学改变书包,但是第一个同学的书包也被改变了。并且通过内存地址可知,他们是同一对象(书包)。原因:调用Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内 容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致clone后的非基本类型变量和原始对 象中相应的变量指向的是同一个对象。 这就是所谓的“影子”克隆。

解决方案:深度克隆,既是对里边的引用也要克隆。以下是实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
class Bag implements Cloneable{//学生的书包
private int width;//宽
private String logo;//品牌
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public String getLogo() {
return logo;
}
public void setLogo(String logo) {
this.logo = logo;
}

@Override
protected Bag clone() throws CloneNotSupportedException {
return (Bag)super.clone();
}

}

public class Student3 implements Cloneable {
private String name;
private int age;
private Bag bag;

public Bag getBag() {
return bag;
}
public void setBag(Bag bag) {
this.bag = bag;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}

@Override
protected Student3 clone() throws CloneNotSupportedException {
Student3 stu = (Student3)super.clone();
stu.bag = bag.clone();
return stu;
}

public static void main(String[] args) {
Student3 stu = new Student3();
stu.setAge(1);
stu.setName("aa");
Bag b = new Bag();
b.setWidth(10);
b.setLogo("Nike");
stu.setBag(b);
printStudent(stu);
try {
Student3 sC = stu.clone();
printStudent(sC);
sC.setAge(12);
sC.setName("bb");
sC.getBag().setWidth(100);//改变书包的属性
sC.getBag().setLogo("JNike");
printStudent(stu);
printStudent(sC);
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

/**
* 输出
* @param stu
*/
private static void printStudent(Student3 stu) {
System.out.println(stu + " age: " + stu.getAge() + " name: " + stu.getName() +
" bag: " + stu.getBag() + "(" + stu.getBag().getLogo() + " width: " +
stu.getBag().getWidth() + ")");
}

}

输出:
testClone.Student3@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike width: 10)
testClone.Student3@7852e922 age: 1 name: aa bag: testClone.Bag@4e25154f(Nike width: 10)
testClone.Student3@15db9742 age: 1 name: aa bag: testClone.Bag@6d06d69c(Nike width: 10)
testClone.Student3@7852e922 age: 12 name: bb bag: testClone.Bag@4e25154f(JNike width: 100)

1.10 什么是反射机制

Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法。这种在运行时动态的获取信息以及动态调用对象的方法的功能称为Java 的反射机制。

Class 类与java.lang.reflect 类库一起对反射的概念进行了支持,该类库包含了Field,Method,Constructor类(每个类都实现了Member 接口)。这些类型的对象时由JVM 在运行时创建的,用以表示未知类里对应的成员。

这样你就可以使用Constructor 创建新的对象,用get() 和set() 方法读取和修改与Field 对象关联的字段,用invoke() 方法调用与Method 对象关联的方法。另外,还可以调用getFields() getMethods() 和 getConstructors() 等很便利的方法,以返回表示字段,方法,以及构造器的对象的数组。这样匿名对象的信息就能在运行时被完全确定下来,而在编译时不需要知道任何事情。

二、获取字节码的方式

在Java 中可以通过三种方法获取类的字节码(Class)对象

  • 通过Object 类中的getClass() 方法,想要用这种方法必须要明确具体的类并且创建该类的对象。

  • 所有数据类型都具备一个静态的属性.class 来获取对应的Class 对象。但是还是要明确到类,然后才能调用类中的静态成员。

  • 只要通过给定类的字符串名称就可以获取该类的字节码对象,这样做扩展性更强。通过Class.forName() 方法完成,必须要指定类的全限定名,由于前两种方法都是在知道该类的情况下获取该类的字节码对象,因此不会有异常,但是Class.forName() 方法如果写错类的路径会报 ClassNotFoundException 的异常。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    ackage com.jas.reflect;

    public class ReflectTest {
    public static void main(String[] args) {

    Fruit fruit = new Fruit();
    Class<?> class1 = fruit.getClass(); //方法一

    Class<?> class2 = Fruit.class; //方法二

    Class class3 = null;
    try { //方法三,如果这里不指定类所在的包名会报 ClassNotFoundException 异常
    class3 = Class.forName("com.jas.reflect.Fruit");
    } catch (ClassNotFoundException e) {
    e.printStackTrace();
    }

    System.out.println(class1 + " " +class2 + " " + class3);

    }
    }

    class Fruit{}

三、通过反射机制获取类信息

通过反射机制创建对象,在创建对象之前要获得对象的构造函数对象,通过构造函数对象创建对应类的实例。

下面这段代码分别在运行期间创建了一个无参与有参的对象实例。由于getConstructor() 方法与newInstance() 方法抛出了很多异常(你可以通过源代码查看它们),这里就简写了直接抛出一个Exception,下同。

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.jas.reflect;

import java.lang.reflect.Constructor;

public class ReflectTest {
public static void main(String[] args) throws Exception {

Class clazz = null;
clazz = Class.forName("com.jas.reflect.Fruit");
Constructor<Fruit> constructor1 = clazz.getConstructor();
Constructor<Fruit> constructor2 = clazz.getConstructor(String.class);

Fruit fruit1 = constructor1.newInstance();
Fruit fruit2 = constructor2.newInstance("Apple");

}
}

class Fruit{
public Fruit(){
System.out.println("无参构造器Run...........");
}
public Fruit(String type){
System.out.println("有参构造器Run..........." + type);
}

}

输出:
无参构造器Run………..
有参构造器Run………..Apple

通过反射机制获取Class 中的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.jas.reflect;

import java.lang.reflect.Field;

public class ReflectTest {
public static void main(String[] args) throws Exception {

Class<?> clazz = null;
Field field = null;

clazz = Class.forName("com.jas.reflect.Fruit");
//field = clazz.getField("num"); getField() 方法不能获取私有的属性
// field = clazz.getField("type"); 访问私有字段时会报 NoSuchFieldException异常
field = clazz.getDeclaredField("type"); //获取私有type 属性
field.setAccessible(true); //对私有字段的访问取消检查
Fruit fruit = (Fruit) clazz.newInstance(); //创建无参对象实例
field.set(fruit,"Apple"); //为无参对象实例属性赋值
Object type = field.get(fruit); //通过fruit 对象获取属性值

System.out.println(type);
}
}

class Fruit{
public int num;
private String type;

public Fruit(){
System.out.println("无参构造器Run...........");
}
public Fruit(String type){
System.out.println("有参构造器Run..........." + type);
}

}

输出:
无参构造器Run………..
Apple

通过反射机制获取Class 中的方法并运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.jas.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectTest {
public static void main(String[] args) throws Exception {

Class clazz = null;
Method method = null;

clazz = Class.forName("com.jas.reflect.Fruit");
Constructor<Fruit> fruitConstructor = clazz.getConstructor(String.class);
Fruit fruit = fruitConstructor.newInstance("Apple"); //创建有参对象实例

method = clazz.getMethod("show",null); //获取空参数show 方法
method.invoke(fruit,null); //执行无参方法

method = clazz.getMethod("show",int.class); //获取有参show 方法
method.invoke(fruit,20); //执行有参方法

}
}

class Fruit{
private String type;

public Fruit(String type){
this.type = type;
}
public void show(){
System.out.println("Fruit type = " + type);
}
public void show(int num){
System.out.println("Fruit type = " + type + ".....Fruit num = " + num);
}
}

输出:
Fruit type = Apple
Fruit type = Apple…..Fruit num = 20

四、反射机制简单应用(使用简单工厂创建对象)

Class.forName() 生成的结果是在编译时不可知的,因此所有的方法特征签名信息都是在执行时被提取出来的。反射机制能过创建一个在编译期完全未知的对象,并调用该对象的方法。

以下是反射机制与泛型的一个应用,通过一个工厂类创建不同类型的实例。

要创建对象的实例类Apple :

1
2
3
4
package com.jas.reflect;

public interface Fruit {}
class Apple implements Fruit{}

加载的配置文件config.properties:

1
Fruit=com.jas.reflect.Apple

工厂类BasicFactory :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.jas.reflect;

import java.io.FileReader;
import java.util.Properties;

public class BasicFactory {
private BasicFactory(){}

private static BasicFactory bf = new BasicFactory();
private static Properties pro = null;

static{
pro = new Properties();
try{
//通过类加载器加载配置文件
pro.load(new FileReader(BasicFactory.class.getClassLoader().
getResource("config.properties").getPath()));
}catch (Exception e) {
e.printStackTrace();
}
}

public static BasicFactory getFactory(){
return bf;
}

//使用泛型获得通用的对象
public <T> T newInstance(Class<T> clazz){
String cName = clazz.getSimpleName(); //获得字节码对象的类名
String clmplName = pro.getProperty(cName); //根据字节码对象的类名通过配置文件获得类的全限定名

try{
return (T)Class.forName(clmplName).newInstance(); //根据类的全限定名创建实例对象
}catch (Exception e) {
throw new RuntimeException(e);
}

}
}

创建对象实例:

1
2
3
4
5
6
7
8
package com.jas.reflect;

public class ReflectTest {
public static void main(String[] args) throws Exception {
Fruit fruit = BasicFactory.getFactory().newInstance(Fruit.class);
System.out.println(fruit);
}
}

输出
com.jas.reflect.Apple@4554617c

上面这个实例通过一个工厂创建不同对象的实例,通过这种方式可以降低代码的耦合度,代码得到了很大程度的扩展,以前要创建Apple 对象需要通过new 关键字创建Apple 对象,如果我们也要创建Orange 对象呢?是不是也要通过new 关键字创建实例并向上转型为Fruit ,这样做是麻烦的。

现在我们直接有一个工厂,你只要在配置文件中配置你要创建对象的信息,你就可以创建任何类型你想要的对象,是不是简单很多了呢?可见反射机制的价值是很惊人的。

Spring 中的 IOC 的底层实现原理就是反射机制,Spring 的容器会帮我们创建实例,该容器中使用的方法就是反射,通过解析xml文件,获取到id属性和class属性里面的内容,利用反射原理创建配置文件里类的实例对象,存入到Spring的bean容器中。

参考书籍:
《Java 编程思想》 Bruce Eckel 著 陈昊鹏 译

1.10 java创建对象的几种方式

  1. 通过new实例化一个对象
  2. 通过反射机制创建对象
  3. 通过clone方法创建一个对象
  4. 通过反序列化方式创建一个对象

1.11 package作用

package的作用

  • package的中文意思是“包”,它是一个比较抽象的逻辑概念,其宗旨是把.java文件 (Java源文件)、.class文件(编译后的文件)以及其他resource文件(例如.xml文件、.avi文件、.mp3文件、.txt文件等)有条理地进行一个组织,以供使用。它类似于Linux文件系统, 有一个根,从根开始有目录和文件,然后目录中嵌套目录。
  • 具体而言,package主要有以下两个作用:
    第一,提供多层命名空间,解决命名冲突,通过使用package,使得处于不同package中的类可以存在相同的名字。
    第二,对类按功能进行分类,使项目的组织更加清晰。当开发一个有非常多的类的项目时,如果不使用package对类进行分类,而是把所有类都放在一个package下,这样的代码不仅可读性差,而且可维护性也不好,会严重影响开发效率。

2)package的用法

  • package的用法一般如下(源文件所在目录为当前目录):
    1.在每个源文件的开头加上”package packagename;”,然后源文件所在目录下创建一个新目录,名称为 packapename。
    2.用javac指令编译每个sourcename. java源文件,将生成的sourcename. classname文件复制到packagename 目录。
    3.用 java 指令运行程序:java packagename. sourcename。

3)实例
以下是一个简单的程序示例。

1
2
3
4
5
6
7
package top.itcourse.pkg;

public class TestPackage {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

结果:
Hello World!

-------------本文结束感谢您的阅读-------------
0%