基础
Q:Integer和int的区别
int是基本数据类型,Integer是类,因为Java很多地方需要类,比如泛型里
Java有自动装箱和自动拆箱
对象类都是final的
-128到127之间,Integer.valueOf返回的是缓存,不新建对象。这个范围可以通过JVM参数设置
Q:Java和C++的区别
Java和C++都是面向对象的,都有封装继承和多态
C++有指针,需要程序员手动释放内存,Java没有指针,不需要手动释放内存
C++支持多继承,Java不支持多继承
Q:重写、重载
重载是一个类里,函数名相同,参数不同,返回值和访问权限不要求
重写是父类和子类之间,对象方法,static方法不行,是基于对象方法的。父类的方法在子类里必须可见,private不行。要求函数名相同,参数相同,返回值子类小于父类,编译异常子类小于父类,访问修饰符,子类大于等于父类。属性没有覆盖的概念,是一种隐藏。
Q:重载的方法,参数个数相同,都是引用类型,调用的时候如果每个参数都传入null,会有什么反应?编译报错?运行报错?
IDE报错,编译不通过
Q:static方法可以被子类重写吗
不可以,不能被重写,只能同名隐藏
父类引用指向子类对象,只会调用父类的静态方法,不具有多态性。可以继承
Q:初始化
局部变量(函数内的变量)不初始化会报错,成员变量有默认的初始值,数组对象是null
Q:instanceof
对象是不是这个类的对象,返回true或false
接口和父类都是true
Q:transient瞬时的
某成员变量有transient,不参与序列化
Q:代码块、静态代码块、构造代码块
代码块:在方法内部,目的是控制变量生命周期,及早释放,避免命名冲突
静态代码块,类里static{},作用是初始化类里的static变量。
在jvm加载类的时候执行,只执行一次,在主方法之前
构造代码块:初始化成员变量,构造方法共同的初始化可以提到构造代码块
每次创建对象时调用,在构造方法之前
Q:Object中的方法
1.clone
2.toString
3.hashcode
4.equals
5.getclass
6.wait
7.notify
8.notifyAll
9.finalize
Q:null
null可以强制转换成任何类,可以用来调用静态方法
((People)null)testMethod();
对象方法会报空指针
Q:实参个数可变
public void test(int....i){}
不确定个实参,可以是0个,可以不传,是一个数组
public void test(int[] i)
会报错,多重定义
Q:参数传递
Java是值传递,对象数组是拷贝地址
Q:实例化方法
1.new
2.clone,不调用构造方法
3.反序列化
4.反射newInstance,会调用无参的构造方法,没有回报错
5.工厂方法:String str = String.valueOf(23)
Q:String
Java虚拟机中有一个字符串池,是共享的,final的,不用担心改变
new String()会在堆里分配,而且不会再放到常量池中,要放进常量池需要intern()方法,这个方法放进常量池,返回常量池对象
Q:字符串内存分配
String s1 = new String("abc"); // 内存分配
String s2 = "abc";
if (s1 == s2) {
// 能不能进来? n
}
String s3 = "abc";
if (s2 == s3) {
// 能不能进来? y
}
s2 = new String("abc");
if (s1 == s2) {
// 能不能进来? y
}
s2 = s1;
s1 = "bcd";
// s2 = ? ab
Q:String、StringBuilder、StringBuffer
String不可变
StringBuilder可变,但是不线程安全,性能好
StringBuffer,线程安全
Q:final、finally、finalize
- final修饰变量、类、方法,代表不可变,final static 代表常量
JVM会对方法,变量,类进行性能优化
final变量必须在声明的时候初始化或者在构造方法初始化
匿名类中所有变量都必须是final的- finally
异常中使用,代表一定会执行,用来断开连接,释放资源等,return会覆盖try,返回
除非try中exit或者退出虚拟机了- finalize
finalize 方法是Object方法,在对象被回收前调用,作用是释放c++层的内存等
如果方法是纯Java写不需要写这个方法,finalize只执行一次,可以对象复活一次
垃圾回收的时候,判断这个对象是否覆盖了finalize方法,没有覆盖直接回收,有finalize方法,且没有执行过就放入F-Queue队列,由一个线程执行该队列中的finalize方法,执行完垃圾回收器再次判断,是否可达,不可达回收,否则复活
如果在执行finalize方法时,出现异常,垃圾回收器不会报异常,只会该对象的finalize执行退出
Q:抽象类和接口的区别
1.接口没有构造方法,抽象类有构造方法
2.抽象类可以有普通方法,接口所有方法都是抽象的
3.抽象类中有普通成员变量,接口没有普通成员变量
4.接口方法只能是public,抽象类可以是public、protected、默认类型
5.接口中可以有静态方法,但是必须实现
接口中可以包含静态成员变量,但是必须是public static final,抽象类都可
Q:多态
父类的引用指向不同的子类对象,可以不修改代码,让引用绑定到不同的类
父类引用可以调用父类中定义的所有属性和方法,只存在子类的方法和属性就不能访问了
内部类
Q:为什么需要内部类
1.通过内部类来隐藏信息
2.实现多重继承
Q:内部类为什么可以访问外部类变量
含有一个外部类的this指针
内部类分:成员内部类、局部内部类、静态内部类、匿名内部类
内部类可以无条件访问外部所有属性和方法,包括private和静态成员
和普通的成员一样
匿名内部类在new后面,只能生成一个对象
编译的时候由系统自动起名Outter$1.class
没有类名,不能写构造方法,编译器会生成一个不带参数的构造方法
要初始化可以用构造代码块
不能有静态方法(没有类名怎么调用啊)
Q:内部类为什么持有外部对象,static内部类为什么没有
内部类会生成一个私有构造函数,传入外部对象
class Outer$Inner{
private Outer&Inner(Outer paramOuter){}
}
这是静态内部类编译后的字节码文件,编译器并没有为它添加额外的构造函数,所以它其实和我们的外部类没有任何关系,这是写在同一个.java源文件中而已.
Q:匿名内部类使用的参数为什么必须是final的
匿名内部类编译后也是单独一个class文件,仅仅有一个外部类的引用,外部传入参数,其实是参数的拷贝,内部的修改并不影响外部。这样从程序员角度看是同一个,内部改变了外部确没变,为了保持一致,所以规定用final避免形参可变。
内部类,实际会在匿名匿名内部类的构造方法中拷贝一份要使用的外部变量。这样,就牵扯两份变量的同步问题,比如内部类实例被回收、匿名内部类被实例化之前外部变量被更改等问题。解决该问题的方法,就是强制使用final,保证引用不可变
Q:内部类为什么可以访问外部private变量
Q:静态内部类和非静态的区别
静态内部类和静态成员变量类似
不能使用外部非静态成员变量或方法
普通内部类都可以方法
静态内部类可以声明普通成员变量和方法,普通内部类不能声明static成员变量和方法
静态内部类可以单独初始化,不依赖于外部
Inner i = new Outer.Inner()
//普通内部类初始化
Outer o = new Outer();
Inner i = o.new Inner();
Q:枚举
枚举也是类,都继承了Enum,每一个变量是类的对象,还是static final修饰的
构造方法是private,没有办法创建枚举值。可以有成员变量,成员方法
public enum Color{ RED,BLUE,BLACK }
编译后
final enum Color{
public static final Color RED;
public static final Color BLUE;
//静态内部类,对对象初始化
static{
new Color[1]
}
private Color(){}
}
继承Enum方法
(1)ordinal():返回枚举的顺序
Color.RED.ordinal(); 返回0
(2)compareTo():比较两个枚举的顺序
(3)values()返回全部枚举的数组
Color[] colors = Color.values();
(4)toString()返回常量的名
Color c = Color.RED;
System.out.println(c); 返回RED
(5)valueOf,返回这个名词的常量
Color.valueOf(“BLUE”); 返回Color.BLUE
Q:IO
字节流InputStream/OutputStream
字符流 Reader/Writer
字符流是字节流读取时查了指定的码表
字节流以字节(8bit)为单位,字符流以字符为单位
字节流能处理所有类型的数据,如图片、AVI,字符流只能处理字符数据
优先使用字节流
反射
Q:什么是反射
反射机制指的是程序在运行时能够获取自身的信息
在Java中,给定类的名字,那么就可以通过反射机制来获得类的所有信息。
Class.forName("com.mysql.jdbc.Driver.class").newInstance();
hibernate、struts都是用反射机制实现的。(android蓝牙)
Q:为什么要有反射,反射是怎么解决这个问题的,Android里如何用的项目里你是如何用反射的
反射可以在运行的时候动态的加载,而不是固定的,增加了灵活性
比如实例化一个对象new Person(),如果想实例化其他的对象,不想修改源码,就可以用反射class.forName(“person”).newInstance(),把加载哪个类写到配置文件中
Q:三种获取Class对象方法
1.Class的静态方法 Class.forName(“java.lang.String”);
2.使用类的.class语法 String.class;
3.对象的getClass()方法
String str = “aa”;
Class<?> classType = str.getClass();
Q:反射创建对象,获取构造器
不带参的构造方法生成对象有两种方式
1.newInstance()直接生成即可,调用无参构造方法
Class<?> clazz = String.class;
Object obj = clazz.newInstance();
2.用Constructor的构造方法调用newInstance(参数)
Class<?> clazz = Customer.class;
//获得无参构造方法
Constructor cons = clazz.getConstructor(new Class[]{});
//通过构造方法来生成对象,构造方法调 con.newInstance()
Object obj = cons.newInstance(new OBject[]{}); //传入参数
如果要通过带参的构造方法生成对象,只有一种方式
Class<?> clazz = Customer.class;
Constructor cons2 = clazz.getConstructor(new Class[]{String.class,int.class});
Object obj2 = cons2.newInstance(new Object[]{“zhangsan”,20});
私有构造方法加一个cons2.setAccessible(true);
Teacher clazz =Teacher.class;
Constrctor cons = clazz.getConstructor(String.class);
Teacher t = (Teacher)cons.newInstance(“aa”);
Q:反射获取私有属性并修改
1.获得CLass
2.创建对象
3.getField或getDeclaredField获取Field
4.修改
Class<?> clazz = Class.forName("com.ang.Teacher");
通过有参构造获取对象,有参构造必须为public类型
Constructor c = clazz.getConstructor(String.class,int.class);//字节码阶段,构造方法的参数只能是字节码对象;
Teacher t = (Teacher) c.newInstance("王艳",100); //创建实例对象
Field field = clazz.getDeclaredField("name");//获取私有成员变量
field.setAccessible(true);//去除私有权限
field.set(t,”李代理”); //传入对象和这个属性的新值
Q:反射如何调用私有方法
Class<?> clazz = Class.forName("com.ang.Teacher");
Constructor c = clazz.getDeclaredConstructor(); //获取私有无参构造
c.setAccessible(true); //去除私有权限
Teacher t = (Teacher) c.newInstance();//通过私有构造获取实例
Method method = clazz.getDeclaredMethod("add", int.class,int.class);//字节码阶段反射有参方法是,需传入参数类型的字节码
method.setAccessible(true); //去除私有权限
int num = (Integer) method.invoke(t, 20,8); //invoke第一个是类的实例,第二个是参数
如果是静态方法,第一个参数,传入null
method.invoke(null,20,8);
getMethods()获得该类所有公有的方法,getDeclaredMethod(parameterTypes)获得该类某个方法,getDeclaredMethods()获得该类所有方法。带有Declared修饰的方法可以反射到私有的方法,没有Declared修饰的只能用来反射公有的方法。其他的Annotation、Field、Constructor也是如此
Q:final成员变量修改
Java反射可以修改final成员变量吗?
分别两种情况
1.在定义的时候就初始化了值,private final String name = “huang”;因为编译器final类型的数据自动被优化了,所有用到的地方都替换成立常量,所以是return “huang”,而不是return this.name。所以不能修改,向阻止编译自动优化,可以改为final String name = (null!=null?”ddd”:”huang”)
2.如果是在构造函数初始化,可以修改
final Class<?> clz = p.getClass();
final Field nameField = clz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(p, String.valueOf("huang.damon"));
equals、hashcode
Q:equals、hashcode
为了提高效率,先判断hashcode,hashcode不相同,equals肯定不相同,就不用算了
如果hashcode相同,再判断equals。这样实际调用equals的次数就大大减少了
在HashSet中的使用,set不允许有相同的对象,就先hashcode,在equals,相同就不存
如果你的类不放在hashcode为基础的容器就不需要重新hashcode
但是如果要放到hashset或者hashmap,他们equals相同,hashcode必须相等
result = 29*getName().hashCode()+getBirthday.hashCode();
Q:怎么重写hashcode方法,String的hashcode方法
用equals中涉及到的属性来计算,equal相同的hashcode要相同
首先定义个初始值,一般来说取17
然后根据属性类型解析
1.boolean: hashcode = a?1:0
2.int,byte,shot,char : hashcode = (int)b
3.long: hashcode = c^c>>>32
4.float: hashcode = d.hashCode()
5.double: hashcode = e.hashCode()
6.引用:若为null则为0,否则递归调用引用的hashcode
7.数组,String:s[0]31 ^ (n-1) + s[1] * 31 ^ (n-2) + ..... + s[n-1]
第三步
result = result31 +hashcode
Q:自定义重写equals的同时还需要重写哪个方法
一般应该重写equals()还有hashCode()
Q:如果没有重写hashcode会发生什么
有些集合是不允许重复元素出现的,这两个方法用来保证集合中没有重复元素
如果只重写了equals方法,两个对象equals返回了true,但是没有重写hashcode方法
集合还是会插入元素,这样集合中就出现重复元素了
hashMap的put方法是先调用hashCode定位到数组的位置,如果该数组的位置上已经存在元素了,即table[i]!=null,那么遍历链表,调用equals方法判断key是否相等,如果equal不相同,说明没有找到这个key,表明这个key不存在,则会插入
如果没有重写hashcode方法,那么就无法定位到同一个数组位置,集合还是会插入元素,这样集合中就出现重复元素了,重写equals就没有意义了
如果重写了hashcode就能定位到数组相同的位置,就可以遍历这条单向链表,用equals判断这两个对象是否相同,如果相同就覆盖,不相同,就插入到链表的头节点处
Q:为什么要有拷贝?解决了什么问题,怎么解决的,什么场景使用
从一个已有的对象,创建一个相似或相同的对象,是模板设计模式,简化对象的创建
值直接拷贝,引用也直接拷贝,不行,怎么办?引用也实现拷贝,遍历值拷贝
或者先把对象序列化,再反序列化,是另一个对象了
浅拷贝:值直接拷贝,引用和对象,拷贝的地址,指向同一块内存,没有新开辟地址
深拷贝:引用和对象新开辟地址,也全部拷贝
clone方法是浅拷贝,implements Cloneable
要实现深拷贝
1.类里的每一个对象都重新clone方法,在顶层类,调用所有对象的clone方法
age是一个成员变量,age里实现clone方法,Student里有age,再调用age的clone方法
2.通过对象的序列化实现深拷贝
先把这个对象序列化,再反序列化给另一个对象
oos.writeObject(stu1);
Student stu2 = (Student)ois.readObject():
clone()默认是深拷贝还是浅拷贝,如何让clone实现深拷贝