面向对象
1.请说明Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?
答:一:重载
概念:重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。最常用的地方就是构造器的重载。
1.被重载的方法必须改变参数列表(参数个数或类型不一样);
2.被重载的方法可以改变返回类型;
3.被重载的方法可以改变访问修饰符;
4.被重载的方法可以声明新的或更广的检查异常;
5.方法能够在同一个类中或者在一个子类中被重载。
6.无法以返回值类型作为重载函数的区分标准。
二:重写
概念:重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。
1.参数列表必须完全与被重写方法的相同。
2.返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
3.访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。(访问权限从大到小为public、protected、default、private)
4.父类的成员方法只能被它的子类重写。
5.声明为 final 的方法不能被重写。
6.声明为 static 的方法不能被重写,但是能够被再次声明。
7.子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
8.重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
9.构造方法不能被重写。
10.如果不能继承一个方法,则不能重写这个方法。
2.请说明Query接口的list方法和iterate方法有什么区别?
答:Hibernate Query接口下的list和iterate方法区别
对于list方法而言,实际上Hibernate是通过一条Select SQL获取所du有的记录。并将其读出,填入到POJO中返回。
而iterate 方法dao,则是首先通过一条Select SQL 获取所有符合查询条件的记录的id,再对这个id 集合进行循环操作,通过单独的Select SQL 取出每个id 所对应的记录,之后填入POJO中返回。
也就是说,对于list 操作,需要一条SQL 完成。而对于iterate 操作,需要n+1条SQL。
- list()方法无法利用一级缓存和二级缓存(对缓存只写不读),它只能在开启查询缓存的前提下使用查询缓存;iterate()方法可以充分利用缓存,如果目标数据只读或者读取频繁,使用iterate()方法可以减少性能开销。
- list()方法不会引起N+1查询问题,而iterate()方法可能引起N+1查询问题
3. 请你谈一下面向对象的"六原则一法则"
OOP的七大原则
- 开闭原则:对扩展开放,对修改关闭(就是说在原有功能上我们不做修改,但是可以添加新的功能)
- 里氏替换原则:父类所拥有的性质在子类也应当具有(但是不建议重写父类的方法,因为这样代码的复用性会变差,所以可以自定义新的方法)
- 依赖倒置原则:我们应当面向接口编程而不是面向实现编程(因为面向接口编程会更为的稳定而具体的实现是多变的耦合程度会高)
- 单一职责原则:一个方法尽可能的只做好一件事情(否则的话耦合程度会变高,粒度会变粗)
- 接口隔离原则:为各个类创建他们所需要的接口
- 合成复用原则:尽量先使用组合聚合关系来实现其次再考虑继承
- 迪米特法则:只与你的朋友交谈不和陌生人交谈,目的是为了降低耦合,降低依赖提高模块的独立性。
4.请说明如何通过反射获取和设置对象私有字段的值?
答:这个问题我首先想到的是单例设计模式通过反射和序列化反序列化进行破除构造器私有。
- 第一步就是通过类的.class的getDeclaredConstructor方法获取到构造器
- 第二步就是将构造器的setAccessible属性设置为true 使得private失效
- 第三步就是实例化对象,判断private被破坏就是实例化的对象不是同一个对象
对于这个问题的话:
private String string="xuan"; //String类型变量私有化
Field declaredField = Demo01.class.getDeclaredField("xuan"); //通过反射获取相应字段
declaredField.setAccessible(true); //设置访问权限 设置为true时表示抑制java权限访问检查
declaredField.set("aa","xuan"); //重新设置字段
5.请说明JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?
答:关键字:throws,throw,try,catch,finally分析
- throws:用于在方法末端处抛异常 通常我们会抛一个比较大的异常(如果异常多的话)
- throw:在执行程序时,可以自主判断会出现什么异常,直接跑出去 throw new xxxException
- try:这里就是写一些会发生异常的代码块
- catch:捕获异常 里面可以对捕获的异常进行处理,可以打印日志信息之类的
- finally:写在这里的代码不论是否异常都会执行,所以我们一般在此处释放资源
try块中可以抛异常,但不常用。-----可以在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另一个try语句保护其他代码。每当遇到一个try语句,”异常“的框架就放到堆栈上面,直到所有的try语句都完成。如果下一级的try语句没有对某种”异常”进行处理,堆栈就会展开,直到遇到有处理这种”异常”的try语句。
6.请你说说Static Nested Class 和 Inner Class的不同
答:就是静态内部类和内部类的不同,也就是加了静态收到什么限制了
个人感觉根据类加载机制,静态加载优先于非静态,所以静态内部类只可以访问本类中含静态关键字static的方法或属性
7.请你讲讲abstract class和interface有什么区别?
答:抽象类:
1、抽象类使用abstract修饰;
2、抽象类不能实例化,即不能使用new关键字来实例化对象;
3、含有抽象方法(使用abstract关键字修饰的方法)的类是抽象类,必须使用abstract关键字修饰;
4、抽象类可以含有抽象方法,也可以不包含抽象方法,抽象类中可以有具体的方法;
5、如果一个子类实现了父类(抽象类)的所有抽象方法,那么该子类可以不必是抽象类,否则就是抽象类;
6、抽象类中的抽象方法只有方法体,没有具体实现
Java8以前的接口:
1、接口使用interface修饰;
2、接口不能被实例化;
3、一个类只能继承一个类,但是可以实现多个接口;
4、接口中方法均为抽象方法;
5、接口中不能包含实例域或静态方法
Java8及以后的接口:增加了default方法和static方法,这两种方法完全可以有方法体
8. 请说明面向对象的特征有哪些方面
答:抽象、封装、继承、多态
- 封装性:举个简单的例子,我们买的手机,我们只需要知道去如何使用它,并不需要知道手机上特
定功能的具体实现。这就是封装性,将不需要对外提供的内容隐藏起来,方便我们去使用。
在java中,方法和private关键字本身就是一种封装 - 继承性:对于继承,在Java中强调的是单继承,一个子类(基类)只能继承一个父类(超类),但
是一个父类是可以被多个子类给继承的,这并不矛盾。当子类继承了父类就相当于子类继承了父类
的属性以及方法。
继承有以下特征:1.继承的关键字为extends;2.子类继承父类需要重写父类的抽象方法。 - 多态性:多态性就很有意思, 例如狗属于动物(即狗类继承动物类) 我就可以 Animal animal
=new Dog();父类引用指向子类对象,这里还有一个特点就是向上转型。 - 抽象性:1、抽象类使用abstract修饰;2、抽象类不能实例化,即不能使用new关键字来实例化对
象;3、含有抽象方法(使用abstract关键字修饰的方法)的类是抽象类,必须使用abstract关键字
修饰;4、抽象类可以含有抽象方法,也可以不包含抽象方法,抽象类中可以有具体的方法;5、如
果一个子类实现了父类(抽象类)的所有抽象方法,那么该子类可以不必是抽象类,否则就是抽象
类;6、抽象类中的抽象方法只有方法体,没有具体实现
9.请说明Comparable和Comparator接口的作用以及它们的区别。
答:首先Comparable接口只有compareTo一个方法
在用法上,你需要写一个类实现Comparable接口,重写compareTo方法 自定义排序规则
public interface Comparable<T> {
public int compareTo(T o);
}
public class User implements Comparable<User>{
private int id;
private String name;
private int price;
public User(int id, String name, int price) {
this.id = id;
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
@Override
public int compareTo(User o) {
if(this.price>o.price){
return 1;
}else if(this.price<o.price){
return -1;
}else {
return 0;
}
}
public static void main(String[] args) {
User[] users = new User[3];
users[0] = new User(1,"寿司",29);
users[1] = new User(2,"蛋挞",21);
users[2] = new User(3,"牛排",25);
Arrays.sort(users);
for (int i = 0; i < users.length; i++) {
System.out.println(users[i]);
}
}
}
//==================================运行结果===============================
User{id=2, name='蛋挞', price=21}
User{id=3, name='牛排', price=25}
User{id=1, name='寿司', price=29}
而Comparator接口有很多方法其中比较重要的是
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
//........
}
List<Student> stus = new ArrayList<Student>(){
{
add(new Student("张三", 30));
add(new Student("李四", 20));
add(new Student("王五", 60));
}
};
// 1.对学生集合按年龄进行排序
Collections.sort(stus, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 升序
//return s1.getAge()-s2.getAge();
return s1.getAge().compareTo(s2.getAge());
// 降序
// return s2.getAge()-s1.getAge();
// return s2.getAge().compareTo(s1.getAge());
}
});
// 2.对学生集合按姓名首字母排序
Comparator comparator = Collator.getInstance(Locale.CHINA);
Collections.sort(stus, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
return comparator.compare(s1.getName(), s2.getName());
}
});
很明显他们都是比较的接口(可以定义排序)
总结:Java提供了只包含一个compareTo()方法的Comparable接口。这个方法可以个给两个对象排序。具体来说,它返回负数,0,正数来表明输入对象小于,等于,大于已经存在的对象。
Java提供了包含compare()和equals()两个方法的Comparator接口。compare()方法用来给两个输入参数排序,返回负数,0,正数表明第一个参数是小于,等于,大于第二个参数。equals()方法需要一个对象作为参数,它用来决定输入参数是否和comparator相等。只有当输入参数也是一个comparator并且输入参数和当前comparator的排序结果是相同的时候,这个方法才返回true。
10.请你谈谈如何通过反射创建对象?
答:反射创建对象有两种方式,基本都是newInstance
- 第一种就是 类名.class.newInstance()返回一个实例化对象
- 第二种就是通过构造方法创建对象 类名.class.getDeclaredConstructor().newInstance()返回一个实例化对象
public class Student{
public static void main(String[] args) throws Exception {
Student student = Student.class.newInstance();
Student student1 = Student.class.getDeclaredConstructor().newInstance();
}
}
11.请你谈谈如何通过反射获取Class对象
答:主要有三种方式
- 第一种,使用 Class.forName 静态方法。前提:已明确类的全路径名。
- 第二种,使用 .class 方法。说明:仅适合在编译前就已经明确要操作的 Class
- 第三种,使用类对象的 getClass() 方法。适合有对象实例的前提下
package javva面向对象;
public class Student{
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("E:\\IdeaProjects\\practice\\src\\javva面向对象");
Class<Student> studentClass = Student.class;
Student student=new Student();
Class<? extends Student> aClass1 = student.getClass();
}
}
12.请你解释一下类加载机制,双亲委派模型,好处是什么?
答 :类加载机制:
-
当某个类加载器收到类加载请求时,会将加载任务委托给父类,依次递归,父类没有完成加载任务最后才到自己去加载。
使用双亲委派模型的好处在于:
-
Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的Bootstrap ClassLoader进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。因此,如果开发者尝试编写一个与rt.jar类库中重名的Java类,可以正常编译,但是永远无法被加载运行。
-
Java 提供三种类型的系统类加载器。第一种是启动类加载器,由C++语言实现,属于JVM的一部分,其作用是加载 <Java_Runtime_Home>/lib 目录中的文件,并且该类加载器只加载特定名称的文件(如 rt.jar),而不是该目录下所有的文件。另外两种是 Java 语言自身实现的类加载器,包括扩展类加载器(ExtClassLoader)和应用类加载器(AppClassLoader),扩展类加载器负责加载**<Java_Runtime_Home>\lib\ext目录中或系统变量 java.ext.dirs 所指定的目录中的文件**。应用程序类加载器负责加载**用户类路径中的文件。**用户可以直接使用扩展类加载器或系统类加载器来加载自己的类,但是用户无法直接使用启动类加载器,除了这两种类加载器以外,用户也可以自定义类加载器,加载流程如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dCGq0Ama-1595236597556)(C:\Users\l\AppData\Roaming\Typora\typora-user-images\image-20200707114430898.png)]
13.请列举你所知道的Object类的方法并简要说明。
答:Object类的方法
- equals方法 判断两个对象是否相等
- hashcode方法:通过本地操作系统获取十进制的哈希值,然后hashcode源码转成16进制
- wait方法 用于线程同步的(线程等待)
- wait(long)方法可以让线程等待一定的时间后自动唤醒
- notify方法 也是用于线程同步的(线程唤醒)
- notifyAll方法 唤醒所有的线程
- toString方法 返回该对象的字符串表示
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
- getClass方法:返回一个对象的运行时类
- clone方法 克隆 创建并返回此对象的一个副本
finalize方法: 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。