面试题_1.线程2.实例化内部类对象

面试题1:设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。
以下程序使用内部类实现线程,对j增减的时候没有考虑顺序问题。

public class ThreadIncDec {

	private int j;

	public static void main(String[] args) {
		ThreadIncDec incDec = new ThreadIncDec();
		Inc inc = incDec.new Inc();
		Dec dec = incDec.new Dec();
		for (int i = 0; i < 2; i++) {
			Thread t = new Thread(inc);
			t.start();
			t = new Thread(dec);
			t.start();
		}
	}
	
	//计数加一
	private synchronized void inc() {
		j++;
		System.out.println(Thread.currentThread().getName() + "-inc:" + j);
	}

	//计数减一
	private synchronized void dec() {
		j--;
		System.out.println(Thread.currentThread().getName() + "-dec:" + j);
	}

	//内部类实现的线程,线程对计数器加一
	class Inc implements Runnable {
		public void run() {
			for (int i = 0; i < 100; i++) {
				inc();
			}
		}
	}

	//内部类实现的线程,线程对计数器减一
	class Dec implements Runnable {
		public void run() {
			for (int i = 0; i < 100; i++) {
				dec();
			}
		}
	}
}

代码片段:

Inc inc = incDec.new Inc();
Dec dec = incDec.new Dec();

内部类怎么实例化对象? 查了资料如下:

实例化静态内部类对象的模板是: 外部类类名.内部类类名 xxx = new 外部类类名.内部类类名()

实例化非静态内部类对象的模板是:外部类类名.内部类类名 xxx = 外部类对象名.new 内部类类名()

 

面试题2:

根据注释填写(1),(2),(3)处的代码

public class Test{
	 public static void main(String[] args){
		 // 初始化Bean1
		// (1)
		 		
		 // 初始化Bean2
		 //(2) 
                                                                        
		 //初始化Bean3
		 //(3)
	 }
	 
	 class Bean1{
		 public int I = 0;
	 }
	 
	 static class Bean2{
		 public int J = 0;
	 }
}

class Bean{
	public class Bean3{
		public int k = 0;
	}
}

这其实就是实例化内部类对象的题。

从上面的题可以看出,Bean1为非静态内部类,Bean2为静态内部类,而Bean3则不是在Test类内部了,而是在一个外部类Bean的内部(是不是很拗口),呵呵。现通过new和反射来详细讲解其产生原理。

1.new

我们知道,内部类分为两种,一种是静态内部类,一种是非静态内部类。前者不用产生外部类的实例化对象即可产生内部类的实例化对象,后者必须先产生外部类的实例化对象,才能产生内部类的实例化对象。

实例化静态内部类对象的模板是: 外部类类名.内部类类名 xxx = new 外部类类名.内部类类名()

实例化非静态内部类对象的模板是:外部类类名.内部类类名 xxx = 外部类对象名.new 内部类类名()

 

public class Test{
	 public static void main(String[] args){
		 //实例化非静态内部类Bean1
		 // (1)
		 Test test = new Test();
		 Test.Bean1 bean1 =test.new Bean1();
		 bean1.I++;
		
		 //实例化静态内部类Bean2
		 //(2)
		 Test.Bean2 bean2 = new Test.Bean2();
		 bean2.J++;

		 //实例化非静态内部类Bean3
		 //(3)
		 Bean bean = new Bean();
		 Bean.Bean3 bean3 = bean.new Bean3();
		 bean3.k++;
	 }
	 
	 class Bean1{
		 public int I = 0;
	 }
	 
	 static class Bean2{
		 public int J = 0;
	 }
}

class Bean{
	public class Bean3{
		public int k = 0;
	}
}

总结:通过new方式产生内部类的实例化对象实现起来比较简单,也很容易理解,如果要深层次了解其产生,请用下面讲解的方式,反射。

2.反射

1>>反射产生非静态内部类Bean1的实例化对象

java代码:

try {
 Class<?> cla2 = Class.forName("com.lovo.nio.Test$Bean1");
 } catch (ClassNotFoundException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }

解析:我们知道,内部类的class文件是以"外部类$内部类.class"的形式存在的,所以获取Class对象的时候,必须使用forName("包名+外部类$内部类")的形式才能得到Class对象

得到Class对象cla2后,肯定有人会说用下面的方法得到Bean1的实例化对象:

Bean1 b6 = (Bean1)cla2.newInstance();
 运行上述代码,出现异常:InstantiationException,查看Java API文档,下面引用其原话:

当应用程序试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常。实例化失败有很多原因,包括但不仅限于以下原因:

  • 类对象表示一个抽象类、接口、数组类、基本类型、void
  • 类没有非 null 构造方法

在这里的原因是:Bean1的构造方法不公开,意思就是说,他的构造方法不是public的,不能通过newInstance()方式产生他的实例化对象。

那么我们是否可以查看其是什么访问修饰符的构造方法呢?答案是可以的,可以通过以下方式得到:

Constructor<?>[] c = cla2.getDeclaredConstructors();
 int i = c[0].getModifiers();
 //得到访问修饰符
 String str = Modifier.toString(i);
 System.out.println(str+" aaaaaaaaa"); //注意:此处的aaaaaaaaaaa仅表示有这个输出

运行以上代码,输出“ aaaaaaaaa”,可以看出并没有输出str,实际上已经输出了,就是default,default在Java中可以省略不写的。现在该明白了吧!~

那要如何才能实例化他的内部类对象呢,刚才我们说过,要实例化非静态的内部类对象,必须先实例化外部类的对象,可是我们任然没有实例化外部类的对象。我们查看JAVA PAI文档,发现Constructor类有一个方法,newInstance(Object... initargs),于是我们想到下面这种方式来构建:

//反射产生非静态内部类Bean1的实例化对象
 try {
 Class<?> cla2 = Class.forName("com.lovo.nio.Test$Bean1");
// Bean1 b6 = (Bean1)cla2.newInstance();
// System.out.println(b6);
 Constructor<?>[] c = cla2.getDeclaredConstructors();
 int i = c[0].getModifiers();
 //得到访问修饰符
 String str = Modifier.toString(i);
 System.out.println(str+" aaaaaaaaa");
 Bean1 bean1 = (Bean1)c[0].newInstance(new Test());
 System.out.println(bean1.I++);
 } catch (ClassNotFoundException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (IllegalArgumentException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (InstantiationException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (IllegalAccessException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (InvocationTargetException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }

 运行以上代码,正常。

2>>反射产生静态内部类Bean2的实例化对象

Java代码:

//反射产生静态内部类Bean2的实例化对象
 try {
 // 初始化Bean2 
 Class<?> cla = null;
 cla = Class.forName("com.lovo.nio.Test$Bean2");
 Bean2 bean2 = (Bean2)cla.newInstance();
 System.out.println(bean2.j++);
 } catch (ClassNotFoundException e1) {
 // TODO Auto-generated catch block
 e1.printStackTrace();
 } catch (InstantiationException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (IllegalAccessException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 解析:

首先来看看static的相关知识:

static内部类意味着: 
 (1) 为创建一个static内部类的对象,我们不需要一个外部类对象。 
 (2) 不能从static内部类的一个对象中访问一个外部类对象. 
 倘若为了创建内部类的对象而不需要创建外部类的一个对象,那么可将所有东西都设为static。为了能正常工作,同时也必须将内部类设为static。 
 此外,也可考虑用一个static内部类容纳自己的测试代码。

在这里我们同样可以使用上面的方法获取他的构造方法的修饰符,也是default的,(静态内部类是有构造方法的,而且是无参的构造方法).

java代码:

try {
 
 // 初始化Bean2 
 Class<?> cla = null;
 cla = Class.forName("com.lovo.nio.Test$Bean2");
 Bean2 bean2 = (Bean2)cla.newInstance();
 Constructor<?>[] cs = cla.getDeclaredConstructors();
// Modifier.toString(cs[0]);
 System.out.println("******************");

 System.err.println(cs.length);
 System.out.println(cs[0].getModifiers());
 System.out.println("******************");

 System.out.println(bean2.j++);
 } catch (ClassNotFoundException e1) {
 // TODO Auto-generated catch block
 e1.printStackTrace();
 } catch (InstantiationException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (IllegalAccessException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }

运行以上代码,红色部分代码输出:

******************
1

******************

可以看出,有一个默认的(方法修饰符没写的,不是没有方法修饰符)构造方法。

如果我们要使用反射产生Bean2的实例化对象的话,只能用getDeclaredConstructors()方法。如上面的代码所示。

3>>反射产生 外部类的内部类Bean3 的实例化对象

分析:要产生外部类的内部类的实例化对象,则要先产生外部类的实例化对象。再通过getClass()方法得到外部类的实例化对象的Class对象,再通过getClasses()方法得到外部类的所有公共类和接口,包括内部类。

Java代码:

try {
 Class<?> c3 = bean.getClass();
 Class<?>[] cl = c3.getClasses();
// Bean3 b4 = (Bean3)c3.newInstance();
// System.out.println(b4);
 //使用反射产生Bean3实例化对象
 Constructor<?>[] cc = cl[0].getDeclaredConstructors();
 //获取构造方法的个数,用以判定其构造方法是否是公共的,如果直接通过c3.newInstance()方法来实例化Bean3的话,则会包异常:java.lang.ClassCastException
 System.out.println(cc.length);
 Bean3 bb = (Bean3)cc[0].newInstance(new Bean());
 System.out.println(bb.k++);
 } catch (IllegalArgumentException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (InstantiationException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (IllegalAccessException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 } catch (InvocationTargetException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值