重载和覆盖的区别,通过反射获取泛型实际类型,枚举要点,五个最常用的集合类之间的区别和联系,final总结

java面试碰到过的题目之方法重载和覆盖的区别。

1. 重载方法必须满足以下条件: 
i. 方法名相同。
ii. 方法的参数类型、个数、顺序至少有一项不同。
iii. 方法的返回类型可以不相同。
iv. 方法的修饰符可以不相同。

2. 重写方法必须满足以下条件: 
i. 子类方法的名称、参数签名和返回类型必须与父类方法的名称、参数签名和返回类型一致。
ii. 子类方法不能缩小父类方法的访问权限。
iii. 子类方法不能抛出比父类方法更多的异常。
iv. 方法覆盖只存在于子类和父类(包括直接父类和间接父类)之间。在同一个类中方法只能被重载,不能被覆盖。
v. 父类的静态方法不能被子类覆盖为非静态的方法。
vi. 子类可以定义与父类的静态方法同名的静态方法,以便在子类中隐藏父类的静态方法。
vii. 父类的非静态方法不能被子类覆盖为静态方法。
viii. 父类的私有方法不能被子类覆盖。
ix. 父类的抽象方法可以被子类通过两种途径覆盖:一是子类实现父类的抽象方法;二是子类重新声明父类的抽象方法。
x. 父类的非抽象方法可以被覆盖为抽象方法。

方法重载和方法覆盖具有以下相同点: 
1)都要求方法名相同。 
2)都可以用于抽象方法和非抽象方法之间。

方法重载和方法覆盖具有以下不同点:
1)方法覆盖要求参数签名必须一致,而方法重载要求参数签名必须不一致。
2)方法覆盖要求返回类型必须一致,而方法重载对此不做限制。 
3)方法覆盖只能用于子类覆盖父类的方法,而方法重载用于同一类的所有方法 
4)方法的覆盖对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制。 
5)父类的一个方法只能被子类覆盖一次,而一个方法在所在的类中可以被重载多次。

=========================================================

通过反射获取泛型实际类型
要求,利用反射技术,对于给定的一个集合例如:Vector v; 这个v接收从某个方法放回的对象,里面存储了某种同一类型对象,而我们现在不知道该v中具体的对象类型,现在使用反射技术获取,方法是写一个方法 如: public static void applyVector(Vector<Date> vector){ } 其中Date可以为任意对象 ,那么从另外一个方法中来获取这个方法中vector中泛型的参数。
代码如下  Method method = Test.class.getMethod("applyVector", Vector.class);     //得到给方法类的
                Type[] types= method.getGenericParameterTypes();                 //得到方法中泛型参数的类型
                ParameterizedType parameterizedType = (ParameterizedType)types[0]; //向下转型
                System.out.println(parameterizedType.getRawType());               //得到源类型 即 Vector
                System.out.println(parameterizedType.getActualTypeArguments()[0]);     //得到泛型的类型 这里获取的是java.util.Date

第一个:既然要得到v中装的对象的类型,却通过方法得到,说明得到v 之后,我们必须知道获得v的这个方法,这是可以知道,但是如果那个方法不是Vector<Date> vector  而是 Vector vector,也就是说他没有指定泛型,那么这程序是会报错的! 而java是兼容可以不指定泛型的。
第二个:既然我们已经有v这个对象了,如果里面确实存有对象,即v.size() > 0  (等于0就没有必要考虑了,那样是没有任何泛型类型的),我们无法直接根据v得到,通过v.getClass() 是得不到的,这样得到的是Vector的类字节码,而不是里面对象的字节码,确实,可是有v对象并且在里面有元素的条件下,我们可以先获得里面的元素,再获得元素的字节码。
代码如下:
             Vector v = new Vector(); //首先定义v,不指定其泛型
             v.add(new Date(System.currentTimeMillis()));//向v中添加对象, 这个v对象可以以任意方式从它出获得,且不知道它里面对象的类型
             System.out.println(v.get(0).getClass().getName()); //在v.size() > 0 的情况下,首先v.get(0)得到其内部元素,这时再getClass()这得到的Class是里面元素的Class?
输出结果:java.util.Date

这样也得不到它的类型,只能得到值的类型,但未必是泛型的类型,因为泛型可以有泛型限定,而不是单一固定类型,所以值的类型有可能是泛型类型的子类。
因为既然不知道里面存的到底是什么类型,只知道是相同的类型,那么从其他的途径获得类型,再放到函数里的验证是不合适的,貌似返回的可以是指定泛型,也可以是指定泛型的子类:

public class ReflectTest {
        public static void main(String[] args) {
                Vector <Number>v = new Vector<Number>();
                v.add(new Integer(8));
                v.add(new Long(1));
        System.out.println(v.get(0).getClass().getName()); 
        System.out.println(v.get(1).getClass().getName()); 
        }
}
运行结果:
java.lang.Integer
java.lang.Long
Integer和long同属于Number

而且就算是不同的包,只要是子类就行:
import java.sql.Time;
import java.util.Date;
import java.util.Vector;

public class ReflectTest1 {
        public static void main(String[] args) {
                Vector <Date>v = new Vector<Date>();
                v.add(new Time(8));
                v.add(new Date(1));
        System.out.println(v.get(0).getClass().getName()); 
        System.out.println(v.get(1).getClass().getName()); 
        }
}
运行结果:
java.sql.Time
java.util.Date
sql中Time是util中Date的子类。

所以通过泛方法获取集合的泛型类型的优势还是很明显的,用于需要准确的返回泛型类型。

=========================================================

枚举类型是单例模式的。你需要实例化一次,然后再整个程序之中就可以调用他的方法和成员变量了。
枚举类型使用单例模式是因为他的值是固定的,不需要发生改变。
枚举类也是一个类当然可以实现一个接口也可以在其中定义构造方法,方法,和内部类

Enum要点
1、在程序中可以使用一个枚举类来指定对象的取值范围。
2、在Java中使用enum关键字定义一个枚举类,每一个枚举类都是继承Enum类。
3、在枚举中可以通过values()方法取得枚举中的全部内容。
4、在枚举类中可以定义构造方法,但在设置枚举范围时必须显式地调用构造方法。
5、所有的枚举类都可以直接使用Comparable进行排序,因为Enum类实现了Comparable接口。
6、Java类集中提供枚举的支持类是EnumMap、EnumSet。
7、一个枚举类可以实现一个接口或者直接定义一个抽象方法,但是每个枚举对象都必须分别实现全部的抽象方法。

=========================================================

Java五个最常用的集合类之间的区别和联系

1.ArrayList: 元素单个,效率高,多用于查询
2.Vector:    元素单个,线程安全,多用于查询
3.LinkedList:元素单个,多用于插入和删除
4.HashMap:   元素成对,元素可为空
5.HashTable: 元素成对,线程安全,元素不可为空

ArrayList
底层是Object数组,所以ArrayList具有数组的查询速度快的优点以及增删速度慢的缺点。
而在LinkedList的底层是一种双向循环链表。在此链表上每一个数据节点都由三部分组成:前指针(指向前面的节点的位置),数据,后指针(指向后面的节点的位置)。最后一个节点的后指针指向第一个节点的前指针,形成一个循环。双向循环链表的查询效率低但是增删效率高。
ArrayList和LinkedList在用法上没有区别,但是在功能上还是有区别的。

LinkedList
经常用在增删操作较多而查询操作很少的情况下:队列和堆栈。
队列:先进先出的数据结构。
栈:后进先出的数据结构。
注意:使用栈的时候一定不能提供方法让不是最后一个元素的元素获得出栈的机会。

Vector
与ArrayList相似,区别是Vector是重量级的组件,使用使消耗的资源比较多。
结论:在考虑并发的情况下用Vector(保证线程的安全)。在不考虑并发的情况下用ArrayList(不能保证线程的安全)。

面试经验(知识点):
java.util.stack(stack即为堆栈)的父类为Vector。可是stack的父类是最不应该为Vector的。因为Vector的底层是数组,且Vector有get方法(意味着它可能访问到并不属于最后一个位置元素的其他元素,很不安全)。对于堆栈和队列只能用push类和get类。Stack类以后不要轻易使用。实现栈一定要用LinkedList。(在JAVA1.5中,collection有queue来实现队列。)

Set-HashSet实现类:
遍历一个Set的方法只有一个:迭代器(interator)。
HashSet中元素是无序的(这个无序指的是数据的添加顺序和后来的排列顺序不同),而且元素不可重复。
在Object中除了有finalize(),toString(),equals(),还有hashCode()。
HashSet底层用的也是数组。当向数组中利用add(Object o)添加对象的时候,系统先找对象的hashCode:
int hc = o.hashCode(); 返回的hashCode为整数值。
Int I = hc%n;(n为数组的长度),取得余数后,利用余数向数组中相应的位置添加数据,以n为6为例,如果I=0则放在数组a[0]位置,如果I=1,则放在数组a[1]位置。如果equals()返回的值为true,则说明数据重复。如果equals()返回的值为false,则再找其他的位置进行比较。这样的机制就导致两个相同的对象有可能重复地添加到数组中,因为他们的hashCode不同。
如果我们能够使两个相同的对象具有相同hashcode,才能在equals()返回为真。
在实例中,定义student对象时覆盖它的hashcode。
因为String类是自动覆盖的,所以当比较String类的对象的时候,就不会出现有两个相同的string对象的情况。
现在,在大部分的JDK中,都已经要求覆盖了hashCode。
结论:如将自定义类用hashSet来添加对象,一定要覆盖hashcode()和equals(),覆盖的原则是保证当两个对象hashcode返回相同的整数,而且equals()返回值为True。
如果偷懒,没有设定equals(),就会造成返回hashCode虽然结果相同,但在程序执行的过程中会多次地调用equals(),从而影响程序执行的效率。

我们要保证相同对象的返回的hashCode一定相同,也要保证不相同的对象的hashCode尽可能不同(因为数组的边界性,hashCode还是可能相同的)。
例子:
public int hashCode(){
return name.hashcode()+age*33;
}
这个例子保证了相同姓名和年龄的记录返回的hashCode是相同的。

使用hashSet的优点:
hashSet的底层是数组,其查询效率非常高。而且在增加和删除的时候由于运用的hashCode的比较确定添加元素的位置,所以不存在元素的偏移,所以效率也非常高。虽然hashSet查询和删除和增加元素的效率都非常高,但是hashSet增删的高效率是通过花费大量的空间换来的:因为空间越大,取余数相同的情况就越小。HashSet这种算法会建立许多无用的空间。使用hashSet类时要注意,如果发生冲突,就会出现遍历整个数组的情况,这样就使得效率非常的低。

=========================================================

一、final 
根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变:设计或效率。 
final类不能被继承,没有子类,final类中的方法默认是final的。  
final方法不能被子类的方法覆盖,但可以被继承。  
final成员变量表示常量,只能被赋值一次,赋值后值不再改变。必须在初始化对象的时候赋初值,否则编译器报错。  
final不能用于修饰构造方法。  
注意:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的??不确定  

1、final类  
final类不能被继承,因此final类的成员方法没有机会被覆盖,默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会载被扩展,那么就设计为final类。  
2、final方法  
如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final方法。  
使用final方法的原因有二:  
第一、把方法锁定,防止任何继承类修改它的意义和实现。  
第二、高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。
3、final变量(常量)  
用final修饰的成员变量表示常量,值一旦给定就无法改变!  
final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。  
另外,final变量定义的时候,可以先声明,而不给初值,这种变量也称为final空白,无论什么情况,编译器都确保空白final在使用之前必须被初始化。但是,final空白在final关键字final的使用上提供了更大的灵活性,为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。

class FinalTest
{
	static{ B = 1002;}

	public final int E; //final空白,必须在初始化对象的时候赋初值 
	public final int b = 0;
	public final static int A;
	public final static int B;

	static{ A = 1001;}

	public FinalTest(int E)
	{ 
		this.E = E; 
	} 

	public static void test2(final int x)
	{
		System.out.println(new FinalTest(120).E);
		System.out.println("x = "+x);
//		x++;
	}
	
	public static void test1(int x)
	{
		final int a;
		a = x;
		System.out.println("a = " + a);
	}

	public static void main(String[] args)
	{
		FinalTest test = new FinalTest(2);
		test.test1(10);
		test.test1(100000);
		test2(190);
	//	test.b = 10;
	}
}
4、final参数  
当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。
5、static和final一块用表示什么  
static final用来修饰成员变量和成员方法,可简单理解为“全局常量”!  
对于变量,表示一旦给值就不可修改,并且通过类名可以访问。  
对于方法,表示不可覆盖,并且可以通过类名直接访问。
=========================================================
在软件工程中,性能分析(performance analysis也称为profiling),是以收集程序运行时信息为手段研究程序行为的分析方法。 这种方法与静态代码分析相对。性能分析的目的在于决定程序的哪个部分应该被优化,从而提高程序的速度或者内存使用效率。
性能分析工具(Profiler)是一个软件工具用于测量程序的执行,特别是函数调用频率和所耗费时间。输出以事件的记录流(踪迹trace,或者事件的数据汇总(轮廓profile)。性能分析工具使用很广泛的技术手段收集数据,包括硬件中断,代码指令,操作系统(钩子),CPU内置的性能计数寄存器等等。性能分析工具的使用称为性能工程学过程。
性能测量数据量与代码大小成线性关系,这是由于数据汇总(profile)的操作通常与产生事件的源代码位置相关。与之对比,事件的踪迹(trace)则与运行时长成线性关系,常使之不符合实际应用。对于顺序执行的程序,通常轮廓就足够了。但并行执行的程序的性能问题(等待消息或者同步问题)常常依赖于事件的关系,因此需要全部的踪迹才能找到问题。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值