结合牛客网的内容和自己的部分理解
https://www.nowcoder.com/tutorial/94/4206176d637541fa92c784a4f547e979
1、● 请你谈谈Java中是如何支持正则表达式操作的?
2、● 请你简单描述一下正则表达式及其用途。
3、● 请你说明一下,在Java中如何跳出当前的多重嵌套循环?
4、● 请你讲讲&和&&的区别?
5、● int和Integer有什么区别?
6、● 我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,请你讲讲如何输出一个某种编码的字符串?
7、● 请你说明String 和StringBuffer的区别
8、● 请你说明一下int 和 Integer 有什么区别
9、● 请你讲讲数组(Array)和列表(ArrayList)的区别?什么时候应该使用Array而不是ArrayList?
10、● 请你解释什么是值传递和引用传递?
11、● 请你解释为什么会出现4.0-3.6=0.40000001这种现象?
12、● 请你讲讲一个十进制的数在内存中是怎么存的?
13、● 请你说明符号==比较的是什么?,==和equals方法的区别
14、● 请你解释Object若不重写hashCode()的话,hashCode()如何计算出来的?
“15、● 请你解释为什么重写equals还要重写hashcode?”
16、集合
17、关键字:final、finally、finalize
18、关键字:static
19、关键字:switch
20、关键字:assert
21、关键字:volatile
22、关键字:Synchronized和lock
23、请你介绍一下Syncronized锁,如果用这个关键字修饰一个静态方法,锁住了什么?如果修饰成员方法,锁住了什么?
24、● 请说明Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?
25、● 请解释Java中的概念,什么是构造函数?什么是构造函数重载?什么是复制构造函数?
26、● 请你谈一下面向对象的"六原则一法则"。
27、反射
28、请说明如何通过反射获取和设置对象私有字段的值?
29.● 请说明内部类可以引用他包含类的成员吗,如果可以,有没有什么限制吗?
30.内部类
31.可以修饰类的修饰符
32、● 请说明JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?
33、异常
34、● 请你说说Static Nested Class 和 Inner Class的不同
35、抽象类和接口的区别
36、为什么接口的成员变量默认都是public static final的
37● 请说明Java是否支持多继承?
38.● 请你说明是否可以在static环境中访问非static变量?
39.● 请你讲讲什么是泛型?
40.● 请解释一下extends 和super 泛型限定符
41.● 请说明静态变量存在什么位置?
42.● 请你解释一下类加载机制,双亲委派模型,好处是什么?
43.● 请你谈谈StringBuffer和StringBuilder有什么区别,底层实现上呢?
44.● 请说明String能否被继承?
45.请说明”static”关键字是什么意思?
46.Java中是否可以覆盖(override)一个private或者是static或者final的方法?
50.● 请列举你所知道的Object类的方法并简要说明。
51.不可变类
52.多态
B1、<< >> >>>
B2、成员变量和局部变量、类变量的区别
1、● 请你谈谈Java中是如何支持正则表达式操作的?
考察点:正则表达式
参考回答:
Java中的String类提供了支持正则表达式操作的方法,包括:matches()、replaceAll()、replaceFirst()、split()。此外,Java中可以用Pattern类和matcher类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class RegExpTest {
public static void main(String[] args) {
String str = "成都市(成华区)(武侯区)(高新区)";
Pattern p = Pattern.compile(".*?(?=\\()");
Matcher m = p.matcher(str);
if(m.find()) {
System.out.println(m.group());
}
}
}
2、● 请你简单描述一下正则表达式及其用途。
考察点:正则表达式
参考回答:
在编写处理字符串的程序时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。绝大多数语言都提供了对正则表达式的支持。比如在python使用爬虫的时候就用到了正则表达式匹配网页URL。
3、● 请你说明一下,在Java中如何跳出当前的多重嵌套循环?
考察点:循环
参考回答:
在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环。(Java中支持带标签的break和continue语句,作用有点类似于C和C++中的goto语句,但是就像要避免使用goto一样,应该避免使用带标签的break和continue)
ok:
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
system.out.println("i="+i+",j="+j);
if(j==5)break ok;
}
}
4、● 请你讲讲&和&&的区别?
考察点:运算符
参考回答:
&运算符有两种用法:(1)按位与;(2)逻辑与。
&&运算符是短路与运算。
按位与:将两个数值的二进制序列按位与。比如7&8–》0111&1000 = 0000
逻辑与:要求运算符左右两端的布尔值都是true整个表达式的值才是true,不管左端的表达式是否为true,都会判断右端的表达式。
短路与:要求运算符左右两端的布尔值都是true整个表达式的值才是true,若左端的表达式为false,则不再判断右侧的表达式。
很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,右端根本不能进行字符串的equals比较,否则会产生NullPointerException异常。
|:(1)按位或;(2)逻辑或。
||:短路或
和&与&&类似
5、● int和Integer有什么区别?
考察点:数据类型
参考回答:
java中的基本类型有9种:8种原始数据类型+void
原始数据类型:boolean,char,byte,short,int,long,float,double
对应的包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
注意,String不是基本数据类型
从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
自动装箱是Java编译器在基本数据类型和对应的对象包装类型之间做的一个转化。比如:把int转化成Integer,double转化成Double,等等。反之就是自动拆箱。
class AutoUnboxingTest {
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3; // 将3自动装箱成Integer类型
int c = 3;
System.out.println(a == b); // false 两个引用没有引用同一对象
System.out.println(a == c); // true a自动拆箱成int类型再和c比较
}
}
8种原始数据类型在声明后立刻会在栈上分配内存空间,除了这8中原始类型外,其余的类型都是引用类型。
注意:java中,默认的小数是double,如果要使用float,可以有两种初始化方法:
f=1.0f;
float f =(float)1.0;
6、● 我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,请你讲讲如何输出一个某种编码的字符串?
考察点:数据类型
参考回答:
Public String translate (String str) {
String tempStr = “”;
try {
tempStr = new String(str.getBytes(“ISO-8859-1″), “GBK”);
tempStr = tempStr.trim();
}
catch (Exception e) {
System.err.println(e.getMessage());
}
return tempStr;
}
String.getBytes(String decode)方法会根据指定的decode编码返回某字符串在该编码下的byte数组表示,与getBytes相对的,可以通过new String(byte[], decode)的方式来还原。
故tempStr = new String(str.getBytes(“ISO-8859-1″), “GBK”);是将ISO-8859-1类型的编码转换成了GBK类型。
7、● 请你说明String 和StringBuffer的区别
考察点:数据类型
参考回答:
String是不可变类,StringBuffer是可变类。
不可变类:
当创建了这个类的实例后,就不允许再修改它的值了。
所有基本类型的包装类都是不可变类,String也是不可变类。
String s ="hello";
s += " world"
System.out.println(s);
输出 hello world
看起来String对象变了,其实没变,只是s这个引用指向了一个新的String对象。
优点:不可变类使用简单、线程安全、节省内存
缺点:不可变类的对象会因为值的不同而产生新的对象,从而导致无法预料的问题。
8、● 请你说明一下int 和 Integer 有什么区别
考察点:数据类型
参考回答:
Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类,是引用类型。
引用类型做参数时,在被调用函数中修改此对象会改变引用对象的值。
原始数据类型做参数时,在被调用函数中修改次对现代格值不会改变原始数据类型的值。
9、● 请你讲讲数组(Array)和列表(ArrayList)的区别?什么时候应该使用Array而不是ArrayList?
考察点:Array
参考回答:
1、Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
2、Array大小是固定的,ArrayList的大小是动态变化的。
3、ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
4、对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
10、● 请你解释什么是值传递和引用传递?
考察点:JAVA引用传递
参考回答:
值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。 所以对引用对象进行操作会同时改变原对象. 原始数据类型的包装类也是引用传递。
11、● 请你解释为什么会出现4.0-3.6=0.40000001这种现象?
考察点:计算机基础
参考回答:
原因简单来说是这样:2进制的小数无法精确的表达10进制小数,计算机在计算10进制小数的过程中要先转换为2进制进行计算,这个过程中出现了误差。
12、● 请你讲讲一个十进制的数在内存中是怎么存的?
考察点:计算机基础
参考回答:
补码的形式。
原反补码:
正数的原反补一样
负数的反码是将原码除了符号位的其余位取反,补码是给反码加1.
例:
原码: 1 0110100
反码: 1 1001011
补码: 1 1001100
13、● 请你说明符号==比较的是什么?,==和equals方法的区别
考点:基础
参考回答:
== 如果两边是基本类型,就是比较数值是否相等。
== 如果两边是对象,则如果两个对象的引用完全相同(指向同一个对象)时, 操作将返回true,否则返回false。
在没有覆盖equals时,equals和==是一样的。
但是如果覆盖了equals,则可以比较两个引用所指向的位置的数据是否一样。
比如String将equals方法覆盖了,所以可以用来比较两个对象的内容是否一致。
14、● 请你解释Object若不重写hashCode()的话,hashCode()如何计算出来的?
考点:基础
参考回答:
Object 的 hashcode 方法是本地方法,也就是用 c 或 c++ 实现的,该方法直接返回对象的内存地址。
如果没有重写hashCode(),则任何对象的hashCode()值都不相等(而hashmap想让部分值的hashCode值一样,所以就要重写)
“15、● 请你解释为什么重写equals还要重写hashcode?”
考点:java基础
参考回答:
由题目14知道没有重写hashCode(),则任何对象的hashCode()值都不相等。
HashMap中的比较key是这样的,先求出key的hashcode(),比较其值是否相等,若相等再比较equals(),若相等则认为他们是相等的。若equals()不相等则认为他们不相等。
如果只重写equals没有重写hashCode(),就会导致相同的key值也被hashcode认为是不同的key值(因为没有重写hashCode(),则任何对象的hashCode()值都不相等),就会在hashmap中存储相同的key值(map中key值不能相同),这就不符合条件了。
equals和hashcode的关系:
1、如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;
2、如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false)
16、集合
java集合(ArrayList/Vector/LinkedList/HashSet/TreeSet/ArrayDeque/PriorityQueue/HashMap/HashTable/ConcurrentHashMap/TreeMap/WeakHashMap)
17、关键字:final、finally、finalize
final:
final用于声明变量、方法、类,分别表示变量不可变、方法不能被重写、类不能被继承。,故final变量必须被初始化
其中,变量不可变有两种:如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象(但是数值可以变)。
finally
finally是异常处理的一部分,用在try/catch语句中,并附带一部分语句块,表示这段语句一定会被执行。
finalize
是Object类的一个方法,在垃圾回收期执行时会调用被回收对象的finalize()方法。
18、关键字:static
static主要有两种作用:
1、位某特定的数据类型或对象分配单一的存储空间,而与创建对象的个数无关。
2、将某个方法或属性与类而不是对象关联在一起。
static主要有4种使用情况:
1、static成员变量(叫做静态变量)
所有此类的对象都可以使用静态变量,静态变量在类加载的使用就被加载了。
注意:在Java中,不能在方法体中定义static变量。(静态方法里也不能定义static变量)
因为在方法体中的变量叫局部变量,在JVM中“局部变量”保存在栈中,而静态变量保存在方法区中。
2、static成员方法
static方法是类的方法。
static方法中只能访问静态成员变量和静态成员方法,因为当static方法被调用时,非静态成员方法和变量可能还没被加载。
static成员方法中不能用this和super,因为this和super不一定被创建了。
3、static代码块
public class Test{
private static int a;
static{
Test.a=1;
Sysout.out.printLn(a);
System.out.printLn("staic block is called");
}
public static vodi main(Stringp[ ]args){
}
}
输出
4
static block id called
4、static内部类
只有内部类才能被定义为staic。
可以不依赖与外部类实例对象而被实例化,静态内部类只能访问外部类中的静态成员和静态方法。
package innerClass;
public class OutClass1 {
public int oid;
public String oname;
public static class InnerStaticClass1{
public int iid;
public String iname;
}
}
package innerClass;
import innerClass.OutClass1.InnerStaticClass1;
public class Test1 {
public static void main(String[] args) {
OutClass1 oc=new OutClass1();
InnerStaticClass1 ic=new InnerStaticClass1(); //不依赖与外部类的实例
}
}
19、关键字:switch
switch(a){
}
其中a必须是整数或者字符类型的变量,不能是float、long、double、string等。(short、byte可以被隐式的转换成int,所以可以使用。)
但是在JDK1.7后添加了对String类型的判断
也就是swithch中只能是int/short/byte/char/string
20、关键字:assert
只需要记住加粗的字就差不多了。
assertion (断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。**它对一个 boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true;**如果该值为false,说明程序已经处于不正确的状态下,系统将给出警告或退出。assert用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后,assertion检查通常是关闭的。
assert有两种形式:
assert expression1;
assert expression1:expression2;
其中expression1是一个boolean表达式,expression2是当expression1为false时输出的信息。
public class Test {
public static void main(String[] args) {
assert 1+1==2;
System.out.println("assert1 ok");
assert 1+1==3:"assert failed,exit";
System.out.println("assert2 ok");
}
}
对于以上代码,执行javac Test.Java和java -ea Test(-ea的意思是打开-ea开关)时,会出现
assert1 ok
assert failed,exit
21、关键字:volatile
当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。
但是,当遇到多线程时,变量的值可能因为别的线程而改变了,而该缓存的值却没有相应的改变,从而造成程序需要的值和实际拿到的值不一样。
volatile用来修饰被多个线程访问和修改的变量,被volatile修饰的变量,每次直接从内存中拿值,不再使用缓存。
并发编程时,经常有三个问题:原子性问题,可见性问题,有序性问题。
1.原子性
即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
Java中只有对基本类型变量的赋值和读取是原子操作
2.可见性
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
3.有序性
有序性:即程序执行的顺序按照代码的先后顺序执行。举个简单的例子,看下面这段代码:
**volatile可以保证可见性和有序性,但是不能保证原子性。(**如果一个变量被volatile修饰了,那么肯定可以保证每次读取这个变量值的时候得到的值是最新的,但是一旦需要对变量进行自增这样的非原子操作,就不会保证这个变量的原子性了。)
volatile并不保证自增的三个运算可以原子性的执行(也就是并不保证取i得值、i值加1,把新i值赋给i这三个操作执行中没有其他线程对i进行操作)
想要符合原子性,就使用Synchronized。
x = 10; //语句1
y = x; //语句2
x++; //语句3
x = x + 1; //语句4
咋一看,有些朋友可能会说上面的4个语句中的操作都是原子性操作。其实只有语句1是原子性操作,其他三个语句都不是原子性操作。
语句1是直接将数值10赋值给x,也就是说线程执行这个语句的会直接将数值10写入到工作内存中。
语句2实际上包含2个操作,它先要去读取x的值,再将x的值写入工作内存,虽然读取x的值以及 将x的值写入工作内存 这2个操作都是原子性操作,但是合起来就不是原子性操作了。
同样的,x++和 x = x+1包括3个操作:读取x的值,进行加1操作,写入新的值。
22、关键字:Synchronized和lock
synchronized是Java的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。JDK1.5以后引入了自旋锁、锁粗化、轻量级锁,偏向锁来有优化关键字的性能。
Lock:JDK5新增了 Lock接口和实现类ReentrantLock(重入锁),利用lock()【阻塞式】,tryLock()【非阻塞式】,lockInterruptibly()【获得了锁就返回,否则线程就休眠,知道获得锁】。
使用lock前先:
Lock lock = new ReentrantLock();
再用lock进行加锁(lock.lock())和释放锁(lock.unlock() )。
区别:
1、Synchronized可以加在方法上,也可以加载代码块上,而Lock要显式的制定起始位置,即lock()和unlock()。Synchronized是托管给JVM的,Lock的锁定是通过代码实现的。
2、竞争不激烈时,Synchronized性能优于ReentrantLock,竞争激烈时,Synchronized性能差。
3、Synchronized会自动释放锁,而Lock需要在finally块中手动释放锁。
23、请你介绍一下Syncronized锁,如果用这个关键字修饰一个静态方法,锁住了什么?如果修饰成员方法,锁住了什么?
synchronized修饰静态方法以及同步代码块的synchronized (类.class)用法锁的是类,线程想要执行对应同步代码,需要获得类锁。
synchronized修饰成员方法,线程获取的是当前调用该方法的对象实例的对象锁。
24、● 请说明Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?
重载:
1、Java中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况(例如,参数数量、参数类型、不同的参数顺序)。
2、重载不能通过访问权限、返回值类型和抛出的异常类型来进行重载。
覆盖(重写):
1、方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表。覆盖者可能不会限制它所覆盖的方法的访问。
2、被覆盖的父类方法不能是private的,否则子类就直说定义了一个方法,并没有覆盖。
由于要满足里氏替换原则(即使用父类的地方都能使用子类进行替换),所以有以下三条性质
3、方法重写时,子类的权限修饰符必须要大于或者等于父类的权限修饰符。 (能使用父类方法的地方就要能使用子类方法)
4、方法重写时,子类的返回值类型必须要小于或者 等于父类的返回值类型。 (子类的返回值必须可以被父类处理)
5、 子类抛出的异常类型要小于或者等于父类抛出的异常类型。 (子类的异常要能被父类处理)
25、● 请解释Java中的概念,什么是构造函数?什么是构造函数重载?什么是复制构造函数?
当新对象被创建的时候,构造函数会被调用。每一个类都有构造函数。在程序员没有给类提供构造函数的情况下,Java编译器会为这个类创建一个默认的构造函数。
Java中构造函数重载和方法重载很相似。可以为一个类创建多个构造函数。每一个构造函数必须有它自己唯一的参数列表。
构造函数必须与类名相同
构造函数不能有返回值(返回值也不能为void)
构造函数不能被继承
子类可以通过super显式的调用父类的构造函数
构造函数的权限修饰符的默认修饰符和当前类的权限修饰符一致
Java不支持像C++中那样的复制构造函数。
26、● 请你谈一下面向对象的"六原则一法则"。
开闭原则、单一职责原则、合成聚合复用原则、接口隔离原则、依赖倒转原则、里氏替换原则、迪米特法则。(开单合接一里地)
*开闭原则:*软件实体应当对扩展开放,对修改关闭。即添加新功能时,不修改原来的代码,这是添加新的代码。
*单一职责原则:*一个类只做它该做的事情,即“高内聚”。
*合成聚合复用原则:*优先使用聚合或合成关系复用代码。
*接口隔离原则:*接口要小而专,绝不能大而全。既然接口表示能力,那么一个接口只应该描述一种能力。
*依赖倒转原则:*面向接口编程。该原则说得直白和具体一些就是声明方法的参数类型、方法的返回类型、变量的引用类型时,尽可能使用抽象类型而不用具体类型,因为抽象类型可以被它的任何一个子类型所替代、
*里氏替换原则:*任何时候都可以用子类型替换掉父类型。
*迪米特法则:*迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解。即“低耦合”。
27、反射
https://baijiahao.baidu.com/s?id=1619748187138646880&wfr=spider&for=pc
反射机制允许程序在运行时进行自我检查,同时也允许对其内部成员进行操作。
一、反射的功能有四种:
1、得到一个对象所属的类
2、获得一个类的所有成员变量和方法
3、在运行时创建对象
4、在运行时调用对象的方法
想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。
二、获取字节码文件对象的三种方式。
1、Class clazz1 = Class.forName(“全限定类名”);
2、Class clazz2 = 类.class;
3、Class clazz3 = 实例.getClass();
我们一般选中第一种方式(Class.forName(“全限定类名”))。
三、通过反射获取类的构造方法、方法以及属性
总结:
//获取构造函数:
c.getConstructor();
//获取类属性
c.getField();
//获取类的方法
c.getMethod();
下面是详细介绍:
1、获取构造函数
结果:
2、获取类属性
结果
这里需要注意,在获取私有属性的时候如果没有进行暴力反射,那么会抛出下面这个异常。
3、获取类中的方法
先定义几个方法
结果:
问:请你谈谈如何通过反射创建对象?
通过类对象调用newInstance()方法,例如:String.class.newInstance()
28、请说明如何通过反射获取和设置对象私有字段的值?
可以通过类对象的getDeclaredField()方法字段(Field)对象,然后再通过字段对象的setAccessible(true)将其设置为可以访问,接下来就可以通过get/set方法来获取/设置字段的值了。
详情看问题27的三、2、获取类属性
29.● 请说明内部类可以引用他包含类的成员吗,如果可以,有没有什么限制吗?
一个内部类对象可以访问创建它的外部类对象的内容,内部类如果不是static的,那么它可以访问创建它的外部类对象的所有属性内部类如果是static的,即为nested class,那么它只可以访问创建它的外部类对象的所有static属性和static方法。
30.内部类
https://www.cnblogs.com/dolphin0520/p/3811445.html
内部类有4种:
静态内部类、成员内部类、局部内部类、匿名内部类。
1、成员内部类
成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式:
class Circle {
double radius = 0;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类
public void drawSahpe() {
System.out.println("drawshape");
}
}
}
2、静态内部类:
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。
静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
}
}
class Outter {
public Outter() {
}
static class Inner {
public Inner() {
}
}
}
3、局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
注意,局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
class People{
public People() {
}
}
class Man{
public Man(){
}
public People getWoman(){
class Woman extends People{ //局部内部类
int age =0;
}
return new Woman();
}
}
4、匿名内部类
匿名内部类也就是没有名字的内部类正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写。
本质:匿名内部类会隐式的继承一个类或者实现一个接口,或者说,匿名内部类是一个继承了该类或者实现了该接口的子类匿名对象。
匿名内部类的前提条件:必须继承一个父类或实现一个接口
实例1:不使用匿名内部类来实现抽象方法
abstract class Person {
public abstract void eat();
}
class Child extends Person {
public void eat() {
System.out.println("eat something");
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Child();
p.eat();
}
}
运行结果:eat something
实例2:匿名内部类的基本实现
abstract class Person {
public abstract void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
运行结果:eat something
实例3:在接口上使用匿名内部类
interface Person {
public void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
注意:
1)匿名内部类不能有构造函数
2)匿名内部类不能定义静态成员、方法、类
3)匿名内部类不能被public、protected、private以及static修饰符的。
4)只能创建匿名内部类的一个实例(因为匿名内部类没有名字,所以只能在new的时候创建一个实例)
5)一个匿名内部类一定在new后面,这个匿名内部类继承一个父类或者实现一个接口。
6)匿名内部类为局部内部类,所以局部内部类的所有限制对匿名内部类都生效。
31.可以修饰类的修饰符
外部类只能被public、默认访问权限(即default)、abstract、final修饰,不能被private和protect修饰。(原因:https://blog.csdn.net/yangyong0717/article/details/78379760)
成员内部类和静态内部类可以被public、默认访问权限(即default)、private、protect、abstract、final修饰。**
这些修饰符只能修饰成员变量,不能修饰局部变量。
局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
匿名内部类也不能被public、protected、private以及static修饰符的。(在某种情况下,匿名内部类也可以认为是一个局部变量)
32、● 请说明JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?
https://www.cnblogs.com/skywang12345/p/3544168.html
在Java中,每个异常都是一个对象,它是Throwable类或其它子类的实例。
• try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
• catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。
• finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
• throw – 用于抛出异常。
• throws – 用在方法签名中,用于声明该方法可能抛出的异常。
try块中可以抛出异常。
33、异常
非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try…catch…finally)这样的异常,也可以不处理。
对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。
检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。**在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。**这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。
RuntimeException 是运行时异常,,常见的有算数异常(如除零异常)、空指针异常、数组越界异常、缓冲区溢出异常
34、● 请你说说Static Nested Class 和 Inner Class的不同
Inner Class(内部类)定义在类中的类。 (一般是JAVA的说法)
Nested Class(嵌套类)是静态(static)内部类。(一般是C++的说法)
35、抽象类和接口的区别
1、接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
2、类可以实现很多个接口,但是只能继承一个抽象类
3、类可以不实现抽象类的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
4、 抽象类可以在不具体实现接口方法的情况下实现(implements)接口。 (因为抽象类的内部还可以有抽象方法)
5、接口的成员变量默认都是public static final的。
6.可以实现多个接口,但只能继承一个抽象类。
7、接口是has-a,抽象类是is-a。
36、为什么接口的成员变量默认都是public static final的
https://blog.csdn.net/keep12moving/article/details/78766555e
public:使接口的实现类可以用这个变量。
static:static修饰就表示它属于类的,随的类的加载而存在的,如果是非static的话,就表示属于对象的,只有建立对象时才有它,而接口是不能建立对象的,所以接口的常量必须定义为static.
final:final修饰就是保证接口定义的常量不能被实现类去修改,如果没有final的话,由子类随意去修改的话,接口建立这个常量就没有意义了。
37● 请说明Java是否支持多继承?
Java中类不支持多继承,只支持单继承(即一个类只有一个父类)。 但是java中的接口支持多继承,,即一个子接口可以有多个父接口。
38.● 请你说明是否可以在static环境中访问非static变量?
不能。
static变量在Java中是属于类的,它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候,会对static变量进行初始化。如果你的代码尝试不用实例来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。
39.● 请你讲讲什么是泛型?
**泛型,即“参数化类型”。**一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参),如果传入其他类型的实参,则不能和类型形参匹配。
泛型可以让集合能够记住集合内元素的类型。如果给集合中传入一个其他类型,则会报错
public class GenericTest {
public static void main(String[] args) {
/*
List list = new ArrayList();
list.add("qqyumidi");
list.add("corn");
list.add(100);
*/
List list = new ArrayList();
list.add("qqyumidi");
list.add("corn");
//list.add(100); // 1 提示编译错误,因为只能传入String类型
for (int i = 0; i < list.size(); i++) {
String name = list.get(i); // 2
System.out.println("name:" + name);
}
}
}
40.● 请解释一下extends 和super 泛型限定符
https://blog.csdn.net/qq_40395278/article/details/88603655
https://www.cnblogs.com/drizzlewithwind/p/6100164.html
1、
<? extends T>限定参数类型的上界:参数类型必须是T或T的子类型
<? super T> 限定参数类型的下界:参数类型必须是T或T的超类型
2、
上界 extends T>不能往里存,只能往外取。即 extends Fruit>会使往盘子里放东西的set( )方法失效。但取东西get( )方法还有效。
因为: extends T>规定了变量eList的类型只能是T的子类,当add的时候,可能会出现add(A)中A的类型是T类型父类的情况。但是get的时候可以用T类型的变量接着。
List extends Number> eList = null;
eList = new ArrayList();
Number numObject = eList.get(0); //语句1,正确
//Type mismatch: cannot convert from capture#3-of ? extends Number to Integer
Integer intObject = eList.get(0); //语句2,错误
//The method add(capture#3-of ? extends Number) in the type List is not applicable for the arguments (Integer)
eList.add(new Integer(1)); //语句3,错误
语句1:List extends Number>eList存放Number及其子类的对象,语句1取出Number(或者Number子类)对象直接赋值给Number类型的变量是符合java规范的。
语句2:List extends Number>eList存放Number及其子类的对象,语句2取出Number(或者Number子类)对象直接赋值给Integer类型(Number子类)的变量是不符合java规范的。
语句3:List extends Number>eList不能够确定实例化对象的具体类型,因此无法add具体对象至列表中,可能的实例化对象如下
下界 super T>不影响往里存,但往外取只能放在Object对象里。即使用下界 super Fruit>会使从盘子里取东西的get( )方法部分失效,只能存放到Object对象里。set( )方法正常。
因为: super T>规定eList的类型是T类型的父类,所以get的时候不清楚到底是哪个父类,就只能用最大的父类Object类型接着。当add的时候不能addT的父类,因为不知道eList的类型到底是什么类型,但是可以addT和T的子类,因为eList中的类型肯定能接住T和T的子类。
List super Integer> sList = null;
sList = new ArrayList();
//Type mismatch: cannot convert from capture#5-of ? super Integer to Number
Number numObj = sList.get(0); //语句1,错误
//Type mismatch: cannot convert from capture#6-of ? super Integer to Integer
Integer intObj = sList.get(0); //语句2,错误
sList.add(new Integer(1)); //语句3,正确
语句1:List super Integer> 无法确定sList中存放的对象的具体类型,因此sList.get获取的值存在不确定性,子类对象的引用无法赋值给兄弟类的引用,父类对象的引用无法赋值给子类的引用,因此语句错误。
语句2:同语句1。
语句3:子类对象的引用可以赋值给父类对象的引用,因此语句正确。
总结:下界类型通配符get方法受限,但可以往列表中添加各种数据类型的对象。因此如果你想把对象写入一个数据结构里,使用 ? super 通配符。限定通配符总是包括自己。
3、
上界 extend Fruit> ,表示所有继承Fruit的子类,但是具体是哪个子类,无法确定,所以调用add的时候,要add什么类型,谁也不知道。但是get的时候,不管是什么子类,不管追溯多少辈,肯定有个父类是Fruit,所以,我都可以用最大的父类Fruit接着,也就是把所有的子类向上转型为Fruit。
下界 super Apple>,表示Apple的所有父类,包括Fruit,一直可以追溯到老祖宗Object 。那么当我add的时候,我不能add Apple的父类,因为不能确定List里面存放的到底是哪个父类。但是我可以add Apple及其子类。因为不管我的子类是什么类型,它都可以向上转型为Apple及其所有的父类甚至转型为Object 。但是当我get的时候,Apple的父类这么多,我用什么接着呢,除了Object,其他的都接不住。
41.● 请说明静态变量存在什么位置?
方法区(详情看JVM)
42.● 请你解释一下类加载机制,双亲委派模型,好处是什么?
虚拟机类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。
类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。
家宴准姐出
其中类加载过程包括加载、验证、准备、解析和初始化五个阶段。
双亲委派模型:
某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
使用双亲委派模型的好处在于Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的Bootstrap ClassLoader进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。因此,如果开发者尝试编写一个与rt.jar类库中重名的Java类,可以正常编译,但是永远无法被加载运行。
43.● 请你谈谈StringBuffer和StringBuilder有什么区别,底层实现上呢?
StringBuffer线程安全,StringBuilder线程不安全,底层实现上的话,StringBuffer其实就是比StringBuilder多了Synchronized修饰符。
44.● 请说明String能否被继承?
String被final修饰,不能被继承。
45.请说明”static”关键字是什么意思?
“static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问。
46.Java中是否可以覆盖(override)一个private或者是static或者final的方法?
都不能。
Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。
50.● 请列举你所知道的Object类的方法并简要说明。
equal()指示某个其他对象是否与此对象“相等”
finalize()当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
getClass()返回一个对象的运行时类。
hashCode()返回该对象的哈希码值。
notify()唤醒在此对象监视器上等待的单个线程。
notifyAll()唤醒在此对象监视器上等待的所有线程。
toString()返回该对象的字符串表示。
wait()导致当前的线程等待
51.不可变类
不可变类是指当创建了这个类的实例后,就不允许修改它的值了。
所有基本类型的包装类都是不可变类,String也是不可变类。
1、保证所有成员变量必须都被private修饰
2、 不提供改变成员变量的方法,包括set
3、确保类中所偶有的方法不会被子类覆盖,可以通过把类定义为final或者吧类中的方法定义为final实现。(不一定会被定义为final,只是定义为final也可以实现)
4、 如果一个类成员不是不可变量,则在get方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝,以防类成员被改变。
5、如果有必要,可以重写equa()和 hashcode()方法,以根据对象的属性值来比较两个对象是否相等。
52.多态
https://www.cnblogs.com/chenssy/p/3372798.html
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,即使是重载该方法,也不能使用。若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法(动态连接、动态调用)。
Java实现多态有三个必要条件:继承、重写、向上转型。
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。
好处:
特点:
1、如果子类中重写了父类的方法,则引用变量必定会调用子类的方法,而不是父类的那个方法了。
2、如果子类中有父类没有的方法,则引用变量不能调用这个方法。
3、当子类重写父类的方法被调用时,只有对象继承链中的最末端(也就是最后的子类)的方法才会被调用。
Object o = new JGJ();
System.out.println(o.toString());
// 输出的结果是Wine : JGJ。
//Object、Wine、JGJ三者继承链关系是:JGJ—>Wine—>Object。
4、其实在继承链中对象方法的调用存在一个优先级:
https://blog.csdn.net/thinkGhoster/article/details/2307001
this.show(O)>super.show(O)>this.show((super)O)>super.show((super)O)
即先看引用类型有没有此方法,再看父类有没有此方法,再看引用类型
public class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
public class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
public class C extends B{
}
public class D extends B{
}
public class Test {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
}
运行结果:
1--A and A a1没用到多态,直接就找A.show(B),结果发现没有A.show(B),所以就找A.show(B的父类),所以就找到了A.show(A)
2--A and A a1没用到多态,直接就找A.show(C),结果发现没有A.show(C),所以就找A.show(C的父类),所以就找到了A.show(A)
3--A and D a1没用到多态,直接就找A.show(D)
4--B and A a2用到了多态,所以a2可能调用A对象的方法,也可能调用B对象的方法,a2.show(b) 我们先在A类中找A.show(B),在A类中没找到A.show(B),就在A类中找A.show(B的父类),找到了A.show(A),结果发现B类(多态中被引用的类)也有show(A)方法,所以就调用B.show(A)。 原因:当多态时,父类的方法被子类重写,若调用此方法,则会调用子类的该方法。
5--B and A a2用到了多态,所以a2可能调用A对象的方法,也可能调用B对象的方法,a2.show(C) 我们先在A类中找A.show(C),在A类中没找到A.show(C),就在A类中找A.show(C的父类),找到了A类中的A.show(A),结果发现B也有show(A)方法,所以就调用B.show(A)。 原因:当多态时,父类的方法被子类重写,若调用此方法,则会调用子类的该方法。
6--A and D a2用到了多态,所以a2可能调用A对象的方法,也可能调用B对象的方法,a2.show(C) 我们先在A类中找A.show(D),找到了A.show(D),而且B类中没有show(D)方法,所以直接调用。
7--B and B b没用到多态,直接调用B.show(B)即可
8--B and B b没用到多态,直接在B类中找show(C),结果在B类中没有show(C),所以就在B的父类,即A类中找有没有show(C),结果发现A类中没有show(C),所以就在B类中找show(C的父类),找到了show(B),故直接调用。
原因:
其实在继承链中对象方法的调用存在一个优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
9--A and D b没用到多态,直接在B类中找show(D),结果在B类中没有show(C),所以就在B的父类,即A类中找有没有show(D),结果发现A类中有show(D),故直接调用。
原因:
其实在继承链中对象方法的调用存在一个优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
首先我们分析5,a2.show©,a2是A类型的引用变量,所以this就代表了A,a2.show©,它在A类中找发现没有找到,于是到A的超类中找(super),由于A没有超类(Object除外),所以跳到第三级,也就是this.show((super)O),C的超类有B、A,所以(super)O为B、A,this同样是A,这里在A中找到了show(A obj),同时由于a2是B类的一个引用且B类重写了show(A obj),因此最终会调用子类B类的show(A obj)方法,结果也就是B and A。(具体分析看上面的网址)
B1、<< >> >>>
<< : 左移运算符,num << 1,相当于num乘以2,左移时不管正负,低位补0
>> : 带符号右移。正数右移高位补0,负数右移高位补1。比如:
4 >> 1,结果是2;-4 >> 1,结果是-2。-2 >> 1,结果是-1。
>>> : 无符号右移。无论是正数还是负数,高位通通补0。
对于正数而言,>>和>>>没区别。
对于负数而言,-2 >>> 1,结果是2147483647(Integer.MAX_VALUE),-1 >>> 1,结果是2147483647(Integer.MAX_VALUE)。
B2、成员变量和局部变量的区别
成员变量:
1、成员变量定义在类中,在整个类中都可以被访问。
2、成员变量随着对象的建立而建立,随着对象的消失而消失,存在于对象所在的堆内存中。
3、成员变量有默认初始化值。
局部变量:
1、局部变量只定义在局部范围内,如:函数内,语句内等,只在所属的区域有效。
2、局部变量存在于栈内存中,作用的范围结束,变量空间会自动释放。
3、局部变量没有默认初始化值
在使用变量时需要遵循的原则为:就近原则
首先在局部范围找,有就使用;接着在成员位置找。
成员变量和类变量的区别
由static修饰的变量称为静态变量,其实质上就是一个全局变量。如果某个内容是被所有对象所共享,那么该内容就应该用静态修饰;没有被静态修饰的内容,其实是属于对象的特殊描述。
不同的对象的实例变量将被分配不同的内存空间, 如果类中的成员变量有类变量,那么所有对象的这个类变量都分配给相同的一处内存,改变其中一个对象的这个类变量会影响其他对象的这个类变量,也就是说对象共享类变量。