面试题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 内部类类名()
面试题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 内部类类名()
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文件是以"外部类$内部类.class"的形式存在的,所以获取Class对象的时候,必须使用forName("包名+外部类$内部类")的形式才能得到Class对象
得到Class对象cla2后,肯定有人会说用下面的方法得到Bean1的实例化对象:
Bean1 b6 = (Bean1)cla2.newInstance();
当应用程序试图使用 Class
类中的 newInstance
方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常。实例化失败有很多原因,包括但不仅限于以下原因:
- 类对象表示一个抽象类、接口、数组类、基本类型、
void
- 类没有非 null 构造方法
在这里的原因是:Bean1的构造方法不公开,意思就是说,他的构造方法不是public的,不能通过newInstance()方式产生他的实例化对象。
那么我们是否可以查看其是什么访问修饰符的构造方法呢?答案是可以的,可以通过以下方式得到:
Constructor<?>[] c = cla2.getDeclaredConstructors();
那要如何才能实例化他的内部类对象呢,刚才我们说过,要实例化非静态的内部类对象,必须先实例化外部类的对象,可是我们任然没有实例化外部类的对象。我们查看JAVA PAI文档,发现Constructor类有一个方法,newInstance(Object...
//
//
//反射产生静态内部类Bean2的实例化对象
static内部类意味着:
在这里我们同样可以使用上面的方法获取他的构造方法的修饰符,也是default的,(静态内部类是有构造方法的,而且是无参的构造方法).
java代码:
try {
//
运行以上代码,红色部分代码输出:
******************
1
******************
可以看出,有一个默认的(方法修饰符没写的,不是没有方法修饰符)构造方法。
如果我们要使用反射产生Bean2的实例化对象的话,只能用getDeclaredConstructors()方法。如上面的代码所示。
分析:要产生外部类的内部类的实例化对象,则要先产生外部类的实例化对象。再通过getClass()方法得到外部类的实例化对象的Class对象,再通过getClasses()方法得到外部类的所有公共类和接口,包括内部类。
Java代码:
try {
//
//