1.1.3 面向对象

1.wait方法底层原理

object中的方法,可以暂停线程,期间会释放对象锁,不像sleep方法,线程休眠期依然持有锁,wait方法的线程,必须调用notify或notifyAll方法唤醒线程!

wait方法会将当前线程放入wait set,等待被唤醒,并放弃lock对象上的所有同步声明。

1、将当前线程封装成ObjectWaiter对象node;

2、通过ObjectMonitor::AddWaiter方法将node添加到_WaitSet列表中;

3、通过ObjectMonitor::exit方法释放当前的ObjectMonitor对象,这样其它竞争线程就可以获取该ObjectMonitor对象。

4、最终底层的park方法会挂起线程;

(最后与之对应的notify方法​)​​​​会随机唤醒_WaitSet中随机一个线程​

2.Java有哪些特性,举个多态的例子。

面向对象编程有三大特性:封装、继承、多态。

//假设有一个类 叫 鸟类,它拥有属性翅膀,拥有方法鸣叫,如下
public class Bird{
private Wing wing;
public void moo(){
System.out.println("鸟叫声");
}
}
//鸟类封装了 翅膀类和moo方法;另外有两个类都继承鸟类并重写了moo方法,分别是鹦鹉和麻雀如下:
//鹦鹉类:
public class Parrot extends Bird{
public void moo(){
System.out.println("鹦鹉的叫声");
}
}
//麻雀类:
public class Sparrow extends Bird{
public void moo(){
System.out.println("麻雀的叫声");
}
}
//方法重写应该懂吧,不懂自己找书看吧;然后你有个妻子她想听鸟叫,就有个妻子类
public class Wife{
public void listen(Bird bird){
bird.moo();
}
/*这时多态就很好的体现了,你妻子想听鸟叫,无论什么鸟都可以给她,但是你想让她和鹦鹉
*说话,你就买了一只鹦鹉传给listen方法,结果你妻子听到了鹦鹉的叫声,程序输出:鹦
*鹉的叫声
*/
public static void main(String[] args) {
new Wife().listen(new Parrot());
}
}
//多态实现了动态绑定,让程序有了很好的扩展性,比如你以后想买一只燕子送给你妻子,
//就只需要写个燕子类Swallow继承Bird方法就可以了,而不需要再在妻子类里添加一个方法listen(Swallow //swallow)

参考 https://zhidao.baidu.com/question/312264064.html

3.String为啥不可变?

必要性:

  • 安全首要原因是安全,不仅仅体现在你的应用中,而且在JDK中,Java的类装载机制通过传递的参数(通常是类名)加载类,这些类名在类路径下,想象一下,假设String是可变的,一些人通过自定义类装载机制分分钟黑掉应用。如果没有了安全,Java不会走到今天
  • 性能 string不可变的设计出于性能考虑,当然背后的原理是string pool,当然string pool不可能使string类不可变,不可变的string更好的提高性能。
  • 线程安全 当多线程访问时,不可变对象是线程安全的,不需要什么高深的逻辑解释,如果对象不可变,线程也不能改变它。 

底层原理:

在JDK1.7中,String类做了一些改动,主要是改变了substring方法执行时的行为,这和本文的主题不相关。JDK1.7中String类的主要成员变量就剩下了两个:

1

2

3

4

5

6

7

public final class String

    implements java.io.Serializable, Comparable<String>, CharSequence {

    /** The value is used for character storage. */

    private final char value[];

 

    /** Cache the hash code for the string */

    private int hash; // Default to 0

value,offset和count这三个变量都是private的,并且没有提供setValue, setOffset和setCount等公共方法来修改这些值,所以在String类的外部无法修改String。也就是说一旦初始化就不能修改, 并且在String类的外部不能访问这三个成员。此外,value,offset和count这三个变量都是final的, 也就是说在String类内部,一旦这三个值初始化了, 也不能被改变。所以可以认为String对象是不可变的了。

特殊情况:

用反射, 可以反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。

参考:https://www.cnblogs.com/wuchanming/p/9201103.html

4.类和对象的区别

1,类是一个抽象的概念,它不存在于现实中的时间/空间里,类只是为所有的对象定义了抽象的属性与行为。就好像“Person(人)”这个类,它虽然可以包含很多个体,但它本身不存在于现实世界上。

2,对象是类的一个具体。它是一个实实在在存在的东西。

3,类是一个静态的概念,类本身不携带任何数据。当没有为类创建任何对象时,类本身不存在于内存空间中。

4,对象是一个动态的概念。每一个对象都存在着有别于其它对象的属于自己的独特的属性和行为。对象的属性可以随着它自己的行为而发生改变。
参考:https://blog.csdn.net/Roger_CoderLife/article/details/81565683

5.请列举你所知道的Object类的方法。

1 clone方法

保护方法,实现对象的浅复制,只有实现了 Cloneable 接口才可以调用该方法,否则抛出 CloneNotSupportedException 异常。

2 getClass 方法

final 方法,获得运行时类的类型。

3 toString 方法

返回一个 String 对象,用来标识自己。该方法用得比较多,一般子类都有覆盖。

4 finalize 方法

该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。

5.equals 方法

该方法是非常重要的一个方法。一般 equals 和 == 是不一样的,但是在 Object 中两者是一样的。子类一般都要重写这个方法。

6 hashCode 方法

该方法用于哈希查找,重写了 equals 方法一般都要重写 hashCode 方法。这个方法在一些具有哈希功能的 Collection 中用到。

一般必须满足 obj1.equals(obj2) == true。 可以推出 obj1.hashCode() == obj2.hashCode(), 但是 hashCode 相等不一定就满足 equals。 不过为了提高效率,应该尽量使上面两个条件接近等价。

2.7 wait 方法

wait 方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait() 方法一直等待,直到获得锁或者被中断。wait(long timeout) 设定一个超时间隔,如果在规定时间内没有获得锁就返回。

调用该方法后当前线程进入睡眠状态,直到以下事件发生。

(1)其他线程调用了该对象的 notify 方法。

(2)其他线程调用了该对象的 notifyAll 方法。

(3)其他线程调用了 interrupt 中断该线程。

(4)时间间隔到了。

此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException 异常。

8 notify 方法

该方法唤醒在该对象上等待的某个线程。

9 notifyAll 方法

该方法唤醒在该对象上等待的所有线程。

参考:https://baijiahao.baidu.com/s?id=1623176133818131097&wfr=spider&for=pc

6.重载和重写的区别?相同参数不同返回值能重载吗?

重载Overload:在同一个类中,允许存在一个以上的同名函数,只要他们的参数个数或者参数类型不同即可。它的特点就是与返回值类型无关,只看参数列表。

重写Override:子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中定义的方法,这相当于把父类中定义的那个完全相同的方法给覆盖掉了。

需要注意的是:
(1)如果父类的方法是private类型,则子类中根本不存在重写,即子类中和父类的private的同名的方法没有重写的关系,因为private的访问权限只限于同一类中,而子类就不会访问到private的方法,所以是子类中增加的一个全新的方法。
(2)因为重载的特点就是与返回值无关,只看参数列表,所以重载是可以改变返回值类型的。但是如果两个方法的参数列表完全一样,是不能通过让他们的返回值类型不同来实现重载的
参考:https://blog.csdn.net/qq_35389417/article/details/80507562

7.”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?

static关键字(静态)最基本的用法是:

1、被static修饰的变量属于类变量,可以通过类名.变量名直接引用,而不需要new出一个类来

2、被static修饰的方法属于类方法,可以通过类名.方法名直接引用,而不需要new出一个类来

3、另外可以编写static代码块来优化程序性能。

(1)静态资源是类初始化的时候加载的,而非静态资源是类new的时候加载的。类的初始化早于类的new,比如Class.forName(“xxx”)方法,就是初始化了一个类,但是并没有new它,只是加载这个类的静态资源罢了。所以对于静态资源来说,它是不可能知道一个类中有哪些非静态资源的;但是对于非静态资源来说就不一样了,由于它是new出来之后产生的,因此属于类的这些东西它都能认识。

(2)静态资源的加载顺序是严格按照静态资源的定义顺序来加载的静态代码块对于定义在它之后的静态变量,可以赋值,但是不能访问静态代码块是严格按照父类静态代码块->子类静态代码块的顺序加载的,且只加载一次

参考: https://www.cnblogs.com/xrq730/p/4820992.html

不可以!

1. private只能够被自身类访问,子类不能访问private修饰的成员,所有不能override一个private方法

2.Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。

8.String能继承吗?

不能,final class String,见上面对String类的说明。

9.StringBuffer和StringBuilder有什么区别,底层实现上呢?

jdk的实现中StringBuffer与StringBuilder都继承自AbstractStringBuilder,对于多线程的安全与非安全看到StringBuffer中方法前面的一堆synchronized。因此会产生线程安全性和执行速度上的区别(相辅相成)

1.线程安全性

StringBuffer类是线程安全的,他的方法都是synchronized关键字修饰的

StringBuilder类是线程不安全的

2.执行速度

StringBuffer线程安全,每个方法都有synchronized同步阻塞,所以操作速度受影响

StringBuilder没有同步阻塞的限制,所以操作速度最快

10.类加载机制,双亲委派模型,好处是什么?

加载

主要完成以下3件事情:
1.通过“类全名”来获取定义此类的二进制字节流
2.将字节流所代表的静态存储结构转换为方法区的运行时数据结构
3.在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口


验证阶段

这个阶段目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。主要包括四种验证:
1.文件格式验证:基于字节流验证,验证字节流是否符合Class文件格式的规范,并且能被当前虚拟机处理。
2.元数据验证:基于方法区的存储结构验证,对字节码描述信息进行语义验证。
3.字节码验证:基于方法区的存储结构验证,进行数据流和控制流的验证。
4.符号引用验证:基于方法区的存储结构验证,发生在解析中,是否可以将符号引用成功解析为直接引用。


准备阶段

仅仅为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值即零值,这里不包含用final修饰的static,因为final在编译的时候就会分配了,同时这里也不会为实例变量分配初始化。类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。


解析阶段

解析主要就是将常量池中的符号引用替换为直接引用的过程。符号引用就是一组符号来描述目标,可以是任何字面量,而直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。有类或接口的解析,字段解析,类方法解析,接口方法解析。这里要注意如果有一个同名字段同时出现在一个类的接口和父类中,那么编译器一般都会拒绝编译。


初始化阶段

初始化阶段依旧是初始化类变量和其他资源,这里将执行用户的static字段和静态语句块的赋值操作。这个过程就是执行类构造器方法的过程。

 

双亲委派模型是每次收到类加载请求时,先将请求委派给父类加载器完成,如果父类加载器无法完成加载,那么子类尝试自己加载

 

好处是保证同一个class,加载出来是同一个类。

参考:https://blog.csdn.net/xu768840497/article/details/79175335

11.静态变量存在哪?

堆中????(各执一词 待考证)

12.讲讲什么是泛型?

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

泛型多用于容器中,往容器中方数据,事先约定什么类型数据,放的时候会检查,不是正确的类型放入时会报错,这样可以建立安全的数据,也避免了强制类型转换。

13.解释extends 和super 泛型限定符-上界不存下界不取

限定通配符总是包括自己
上界类型通配符extends (存放Number及其子类的对象):add方法受限(只能传入本类和子类,不知道子类的具体类型,不能add,但是get可以用NUmber类型赋值)
下界类型通配符super :get方法受限(只能传入本类和父类,传的时候知道怎么传get的时候不知道具体是哪一个兄弟类或者是不是父类)
如果你想从一个数据类型里获取数据,使用 ? extends 通配符
如果你想把对象写入一个数据结构里,使用 ? super 通配符
如果你既想存,又想取,那就别用通配符
不能同时声明泛型通配符上界和下界

14.是否可以在static环境中访问非static变量?

不行,static修饰后在类加载的时候已经初始化了,而非static变量在new的时候才初始化,也就是说在执行static修饰的代码的时候,非static的变量还没有初始化。

我们都知道被static修饰的变量是属于这个类的,它不依赖于它所属于的类的具体实例,它在所有的实例中的值是一样的。当类被 Java 虚拟机载入 的时候,会对 static 变量进行初始化。如果你的代码尝试不用实例来访问非 static 的变量, 编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上

第二段转载自 https://blog.csdn.net/dragon901/article/details/79868239

15.谈谈如何通过反射创建对象?

1.得到相应的 类对象

class 里面有个方法 Class c= Class.forName(className),通过这个方法可以得到相应的 类信息

比如String的className 为 java.lang.String

2.通过类对象得到 所表示的类的实例

2.1创建不带任何参数的对象(构造函数没有参数列表)

举例: Class c=Class.forName("java.lang.String");

            String s=(String)c.newInstance();

2.1创建带参数的对象

通过类对象的getConstructor()或getDeclaredConstructor()方法获得构造器(Constructor)对象并调用其newInstance()方法创建对象.

         Class<?> clz = Class.forName("test.java.zxj.test_project.User");
        //获取参数为<String,String>的构造器
        Constructor<?> constructor = clz.getConstructor(String.class, String.class);(类中带有两个String类型参数的构造器)
        Object object = constructor.newInstance("userId_001","name_jack");
参考:https://blog.csdn.net/qq_33236248/article/details/80347884

https://blog.csdn.net/omrlai1/article/details/78086382

16.Java支持多继承么?

java不支持多继承,但可以实现多个接口,c++支持多继承。

17.接口和抽象类的区别是什么?

定义:

含有 abstract 修饰符 class 即为抽象类,抽象类不能创建实际对象,含有抽象方法的抽象类必须定义为 abstract class。

接口可以说成是一种特殊的抽象类,接口中的所有方法都必须是抽象的,接口中的方法定义默认为 public abstract 类型,接口中的成员产量类型默认为 public static final。

区别:

a. 抽象类可以有构造方法,接口中不能有构造方法。

b. 抽象类中可以有普通成员变量,接口中没有普通成员变量。

c. 抽象类中可以包含非抽象普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的方法。

d. 抽象类中的抽象方法的访问权限可以是 public、protected 和(默认类型,虽然 eclipse 不报错,但也不能用,默认类型子类不能继承),接口中的抽象方法只能是 public 类型的,并且默认即为 public abstract 类型。

e. 抽象类中可以包含静态方法,在 JDK1.8 之前接口中不能不包含静态方法,JDK1.8 以后可以包含。

f. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问权限可以是任意的,但接口中定义的变量只能是 public static final 类型的,并且默认即为 public static final 类型。

g. 一个类可以实现多个接口,用逗号隔开,但只能继承一个抽象类,接口不可以实现接口,但可以继承接口,并且可以继承多个接口,用逗号隔开。

18.Comparable和Comparator接口是干什么的?列出它们的区别。

Comparable和Comparator都是用来实现集合中元素的比较、排序的。
Comparable是在集合内部定义的方法实现的排序,位于java.lang下。
Comparator是在集合外部实现的排序,位于java.util下。

Comparable是一个对象本身就已经支持自比较所需要实现的接口,如String、Integer自己就实现了Comparable接口,可完成比较大小操作。自定义类要在加入list容器中后能够排序,也可以实现Comparable接口,在用Collections类的sort方法排序时若不指定Comparator,那就以自然顺序排序。所谓自然顺序就是实现Comparable接口设定的排序方式。


Comparator是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足要求时,可写一个比较器来完成两个对象之间大小的比较。Comparator体现了一种策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。

总而言之Comparable是自已完成比较,Comparator是外部程序实现比较。

Comparable接口将比较代码嵌入自身类中,而后者在一个独立的类中实现比较。

参考 https://blog.csdn.net/woshixuye/article/details/22326981

19.面向对象的特征有哪些方面

面向对象的编程语言有封装、继承 、抽象、多态等4个主要的特征。

1封装: 封装是保证软件部件具有优良的模块性的基础,封装的目标就是要实现软件部件的“高内聚、低耦合”,防止程序相互依赖性而带来的变动影响。在面向对象的编程语言中,对象是封装的最基本单位,面向对象的封装比传统语言的封装更为清晰、更为有力。面向对象的封装就是把描述一个对象的属性和行为的代码封装在一个“模块”中,也就是一个类中,属性用变量定义,行为用方法进行定义,方法可以直接访问同一个对象中的属性。通常情况下,只要记住让变量和访问这个变量的方法放在一起,将一个类中的成员变量全部定义成私有的,只有这个类自己的方法才可以访问到这些成员变量,这就基本上实现对象的封装,就很容易找出要分配到这个类上的方法了,就基本上算是会面向对象的编程了。把握一个原则:把对同一事物进行操作的方法和相关的方法放在同一个类中,把方法和它操作的数据放在同一个类中。 

2.  抽象: 抽象就是找出一些事物的相似和共性之处,然后将这些事物归为一个类,这个类只考虑这些事物的相似和共性之处,并且会忽略与当前主题和目标无关的那些方面,将注意力集中在与当前目标有关的方面。例如,看到一只蚂蚁和大象,你能够想象出它们的相同之处,那就是抽象。抽象包括行为抽象和状态抽象两个方面。例如,定义一个Person类,

如下: class Person{ String name; int age; } 人本来是很复杂的事物,有很多方面,但因为当前系统只需要了解人的姓名和年龄,所以上面定义的类中只包含姓名和年龄这两个属性,这就是一种抽像,使用抽象可以避免考虑一些与目标无关的细节。我对抽象的理解就是不要用显微镜去看一个事物的所有方面,这样涉及的内容就太多了,而是要善于划分问题的边界,当前系统需要什么,就只考虑什么。

3.  继承: 在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并可以加入若干新的内容,或修改原来的方法使之更适合特殊的需要,这就是继承。继承是子类自动共享父类数据和方法的机制,这是类之间的一种关系,提高了软件的可重用性和可扩展性。

4.多态: 多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。多态性增强了软件的灵活性和扩展性。例如,下面代码中的UserDao是一个接口,它定义引用变量userDao指向的实例对象由daofactory.getDao()在执行的时候返回,有时候指向的是UserJdbcDao这个实现,有时候指向的是UserHibernateDao这个实现,这样,不用修改源代码,就可以改变userDao指向的具体类实现,从而导致userDao.insertUser()方法调用的具体代码也随之改变,即有时候调用的是UserJdbcDao的insertUser方法,有时候调用的是UserHibernateDao的insertUser方法: UserDao userDao = daofactory.getDao(); userDao.insertUser(user);

参考:https://www.cnblogs.com/guweiwei/p/6599289.html

20.final, finally, finalize的区别。

1. final 

  在java中,final可以用来修饰类,方法和变量(成员变量或局部变量)。

1.1 修饰类

  当用final修饰类的时,表明该类不能被其他类所继承。当我们需要让一个类永远不被继承,此时就可以用final修饰,但要注意:

final类中所有的成员方法都会隐式的定义为final方法。

1.2 修饰方法

使用final方法的原因主要有两个:

  (1) 把方法锁定,以防止继承类对其进行更改。

  (2) 效率,在早期的java版本中,会将final方法转为内嵌调用。但若方法过于庞大,可能在性能上不会有多大提升。因此在最近版本中,不需要final方法进行这些优化了。

final方法意味着“最后的、最终的”含义,即此方法不能被重写。

注意:若父类中final方法的访问权限为private,将导致子类中不能直接继承该方法,因此,此时可以在子类中定义相同方法名的函数,此时不会与重写final的矛盾,而是在子类中重新地定义了新方法。

1.3 修饰变量

   final成员变量表示常量,只能被赋值一次,赋值后其值不再改变。类似于C++中的const。

  当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。 

  final修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。

2. finally

   一般情况下,finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下。

但是有部分特殊情况:https://www.cnblogs.com/ktao/p/8586966.html

3. finalize  

  finalize()是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。 
特殊情况下,需要程序员实现finalize,当对象被回收的时候释放一些资源,比如:一个socket链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现finalize,关闭这个链接。 
  使用finalize还需要注意一个事,调用super.finalize();

  一个对象的finalize()方法只会被调用一次,而且finalize()被调用不意味着gc会立即回收该对象,所以有可能调用finalize()后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用finalize(),产生问题。 所以,推荐不要使用finalize()方法,它跟析构函数不一样。

参考:https://www.cnblogs.com/ktao/p/8586966.html

21.Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型?

重载与重写的区别已做回答。

这个问题很模糊,如果参数不变只边返回值类型是不能实现重载的。

但是如果参数变了,并且改变了返回值的类型当然可以实现重载。

22.abstract class和interface有什么区别?

抽象类与接口的区别上文已做回答

23.Static Nested Class 和 Inner Class的不同

tatic Nested Class(嵌套类) 和 Inner Class(内部类)

可以在一个类的内部定义另一个类, 这种类称为嵌套类(nested classes),它有两种类型: 
静态嵌套类和非静态嵌套类.静态嵌套类使用很少, 最重要的是非静态嵌套类, 也即是被称作为
内部类(inner).其中inner类又可分为三种: 
(1) 在一个类(外部类)中直接定义的内部类;
(2) 在一个方法(外部类的方法)中定义的内部类;
(3) 匿名内部类.

1. 静态嵌套类

 static class Person
   {
     private String address = "China";
     public String mail = "kongbowoo@yahoo.com.cn";//内部类公有成员

     public void display()
     {
       //System.out.println(num);//不能直接访问外部类的非静态成员
       System.out.println(name);//只能直接访问外部类的静态成员
       System.out.println("Inner " + address);//访问本内部类成员。
     }
   }
在静态嵌套类内部, 不能访问外部类的非静态成员, 这是由Java语法中"静态方法不能直接访问非静态成员"所限定.若想访问外部类的变量, 必须通过其它方法解决, 由于这个原因, 静态嵌套类使用很少.注意, 外部类访问内部类的的成员有些特别, 不能直接访问, 但可以通过内部类实例来访问, 这是因为静态嵌套内的所有成员和方法默认为静态的了.同时注意, 内部静态类Person只在类StaticTest 范围内可见, 若在其它类中引用或初始化, 均是错误的.

2.在外部类中定义内部类

class InnerTwo
   {
     InnerOne innerx = getInnerOne();// 可以访问
     public void show()
     {
       // System.out.println(inner_y); // 不可访问Innter的y成员
       // System.out.println(Inner.inner_y);   // 不可直接访问Inner的任何成员和方法
       innerx.display();// 可以访问
       innerx.display2();// 可以访问
       System.out.println(innerx.inner_y);// 可以访问
       System.out.println(innerx.inner_z);// 可以访问
       System.out.println(innerx.inner_m);// 可以访问
     }
   }

内部类Inner及InnterTwo只在类Outer的作用域内是可知的, 如果类Outer外的任何代码尝试初始化类Inner或使用它, 编译就不会通过.同时, 内部类的变量成员只在内部内内部可见, 若外部类或同层次的内部类需要访问, 需采用示例程序
中的方法, 不可直接访问内部类的变量.

3.在方法中定义内部类

在方法内部的内部类的可见性更小, 它只在方法内部可见, 在外部类(及外部类的其它方法中)中都不可见了.同时, 它有一个特点, 就是方法内的内部类连本方法的成员变量都不可访问, 它只能访问本方法的final型成员.同时另一个需引起注意的是方法内部定义成员, 只允许final修饰或不加修饰符, 其它像static等均不可用.

4.匿名内部类

 addMouseListener(new MouseAdapter()
     {
       public void mousePressed(MouseEvent me)
       {
         showStatus("Mouse Pressed!");
       }
     });
方法addMouseListener接受一个对象型的参数表达式, 于是, 在参数里, 我们定义了一个匿名内部类,这个类是一个MouseAdapter类型的类, 同时在这个类中定义了一个继承的方法mousePressed, 整个类做为一个参数.这个类没有名称, 但是当执行这个表达式时它被自动实例化.同时因为, 这个匿名内部类是定义在AnonymousInnerClassDemo类内部的, 所以它可以访问它的方 showStatus.这同前面的内部类是一致的.

参考:https://blog.csdn.net/machinecat0898/article/details/80071242

24.当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。
  Java参数,不管是原始类型还是引用类型,传递的都是副本(有另外一种说法是传值,但是说传副本更好理解吧,传值通常是相对传址而言)。
  如果参数类型是原始类型,那么传过来的就是这个参数的一个副本,也就是这个原始参数的值,这个跟之前所谈的传值是一样的。如果在函数中改变了副本的值不会改变原始的值.
  如果参数类型是引用类型,那么传过来的就是这个引用参数的副本,这个副本存放的是参数的地址。如果在函数中没有改变这个副本的地址,而是改变了地址中的 值,那么在函数内的改变会影响到传入的参数。如果在函数中改变了副本的地址,如new一个,那么副本就指向了一个新的地址,此时传入的参数还是指向原来的 地址,所以不会改变参数的值。
基本类型参数传递:不改变值
原文:https://blog.csdn.net/kavito/article/details/80455906

25.Java的接口和C++的虚类的相同和不同处。

C++虚类相当于java中的抽象类,与接口的不同处是:

  1.一个子类只能继承一个抽象类(虚类),但能实现多个接口

  2.一个抽象类可以有构造方法,接口没有构造方法

  3.一个抽象类中的方法不一定是抽象方法,即其中的方法可以有实现(有方法体),接口中的方法都是抽象方法,不能有方法体,只有方法声明

  4.一个抽象类可以是public、private、protected、default,接口只有public

  5.一个抽象类中的方法可以是public、private、protected、default,接口中的方法只能是public和default修饰,实际上都是public的abstract方法

 

相同之处是:

  都不能实例化。

 

 补充

  接口是一类特殊的抽象类,是更抽象的抽象类,你可以这样理解。抽象类是一个不完整的类,接口只定义了一些功能。

参考:https://www.cnblogs.com/HeartStarer/p/8760864.html

实际上就是在问java中的接口和抽象类的区别

26.JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?

throws是获取异常
throw是抛出异常
try是将会发生异常的语句括起来,从而进行异常的处理,
catch是如果有异常就会执行他里面的语句,
而finally不论是否有异常都会进行执行的语句。

 

当然可以

https://www.cnblogs.com/guweiwei/p/6612052.html

27.内部类可以引用他包含类的成员吗?有没有什么限制?

完全可以。如果不是静态内部类,那没有什么限制! 一个内部类对象可以访问创建它的外部类对象的成员包括私有成员。

28.两个对象值相同(x.equals(y) == true),但却可有不同的hash code说法是否正确?

不对,如果两个对象x 和 y 满足 x.equals(y) == true,它们的哈希码(hashCode)应当相同。

Java 对于eqauls 方法和 hashCode 方法是这样规定的:

(1)如果两个对象相同(equals 方法返回 true),那么它们的hashCode 值一定要相同;

(2)如果两个对象的 hashCode 相同,它们并不一定相同。

当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在 Set 集合中,同时增

加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。

首先equals 方法必须满足自反性(x.equals(x)必须返回 true)、对称性(x.equals(y)返回 true 时,y.equals(x)也必须返回 true)、传递性(x.equals(y)和 y.equals(z)都返回 true 时,x.equals(z)也必须返回 true)和一致性(当x 和 y 引用的对象信息没有被修改时,多次调用 x.equals(y)应该得到同样的返回值),而且对于任何非 null值的引用 x,x.equals(null)必须返回false。

实现高质量的equals方法的诀窍包括:

    1. 使用==操作符检查"参数是否为这个对象的引用";

    2. 使用 instanceof 操作符检查"参数是否为正确的类型";

    3. 对于类中的关键属性,检查参数传入对象的属性是否与之相匹配;

    4. 编写完 equals 方法后,问自己它是否满足对称性、传递性、一致性;

    5. 重写 equals 时总是要重写 hashCode;

    6. 不要将 equals 方法参数中的 Object 对象替换为其他的类型,在重写时不要忘掉@Override 注解。
参考:https://blog.csdn.net/chang384915878/article/details/79528518

29.重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?

上文已有,不能。

在《深入理解Java虚拟机》中,6.3.6章节有这样一段:

    在Java语言中,要重载一个方法,除了要与原方法具有相同的简单名称之外,还要求必须拥有一个与原方法不同的特征签名;

    特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合,也就是因为返回值不会包含在特征签名之中,因此Java语言里面是无法仅仅依靠返回值的不同来对一个已有方法进行重载。

    但在Class文件格式之中,特征签名的范围更大一些,只要描述符不是完全一致的两个方法也可以共存。

    也就是说,如果两个方法有相同的名称和特征签名,但返回值不同,那么也是可以合法存于同一个Class文件中的。

Class文件中同方法名、同参数、不同返回值可以,那为什么Java文件中不行呢?

因为Java语言规范的规定,所以编译时会出现错误。

那为什么Class文件可以呢?因为Java虚拟机规范和Java语言规范不同,两者是分开的...
参考:https://blog.csdn.net/simba_cheng/article/details/80835646

30.如何通过反射获取和设置对象私有字段的值?

可以通过类对象的getDeclaredField()方法字段(Field)对象,然后再通过字段对象的setAccessible(true)将其设置为可以访问,接下来就可以通过get/set方法来获取/设置字段的值了。

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;


 


/**


 *
 反射工具类


 */


public class ReflectionUtil {


 


    private ReflectionUtil() {
    throw new AssertionError();


    }


 


    /**


     *
 通过反射取对象指定字段(属性)的值


     *
 @param target 目标对象


     *
 @param fieldName 字段的名字


     *
 @throws 如果取不到对象指定字段的值则抛出异常


     *
 @return 字段的值


     */


    public static Object getValue(Object target, String fieldName) {
    Class<?> clazz = target.getClass();
    String[] fs = fieldName.split("\\.");
    try{


            for(int i = 0;i < fs.length - 1;i++) {
                Field f = clazz.getDeclaredField(fs[i]);
                f.setAccessible(true);
                target= f.get(target);
                clazz = target.getClass();
                }
            Field f = clazz.getDeclaredField(fs[fs.length - 1]);
            f.setAccessible(true);
            return f.get(target);
        }catch(Exception e) {
            throw new RuntimeException(e);
        }
    }


 


    /**


     *
 通过反射给对象的指定字段赋值


     *
 @param target 目标对象


     *
 @param fieldName 字段的名称


     *
 @param value 值


     */


    public static void setValue(Object target, String fieldName, Object value) {
        Class<?> clazz = target.getClass();
        String[] fs = fieldName.split("\\.");
        try{
            for(int i = 0; i < fs.length - 1; i++) {
                Field f = clazz.getDeclaredField(fs[i]);
                f.setAccessible(true);
                Object val = f.get(target);
                if(val == null){
                    Constructor<?> c = f.getType().getDeclaredConstructor();
                    c.setAccessible(true);
                    val = c.newInstance();
                    f.set(target, val);
                }
                target = val;
                clazz = target.getClass();
            }
            Field f = clazz.getDeclaredField(fs[fs.length - 1]);
            f.setAccessible(true);
            f.set(target, value);
        }catch(Exception e) {
            throw new RuntimeException(e);
        }
    }


 


}

参考:https://blog.csdn.net/oMrLai1/article/details/78086419 

https://blog.csdn.net/u013604031/article/details/50982722

31.谈一下面向对象的"六原则一法则"。

链接:https://www.nowcoder.com/questionTerminal/f2b5015e83b7420f9567bbe27c1f7009?orderByHotValue=1&page=1&onlyReference=false
来源:牛客网

(1)单一职责原则:一个类只做它该做的事情。(单一职责原则想表达的就是"高内聚",写代码最终极的原则只有六个字"高内聚、低耦合",所谓的高内聚就是一个代码模块只完成一项功能,在面向对象中,如果只让一个类完成它该做的事,而不涉及与它无关的领域就是践行了高内聚的原则,这个类就只有单一职责。
(2)开闭原则:软件实体应当对扩展开放,对修改关闭。(在理想的状态下,当我们需要为一个软件系统增加新功能时,只需要从原来的系统派生出一些新类就可以,不需要修改原来的任何一行代码。要做到开闭有两个要点:①抽象是关键,一个系统中如果没有抽象类或接口系统就没有扩展点;②封装可变性,将系统中的各种可变因素封装到一个继承结构中,如果多个可变因素混杂在一起,系统将变得复杂而混乱。
(3)依赖倒转原则:面向接口编程。(该原则说得直白和具体一些就是声明方法的参数类型、方法的返回类型、变量的引用类型时,尽可能使用抽象类型而不用具体类型,因为抽象类型可以被它的任何一个子类型所替代。
(4)里氏替换原则:任何时候都可以用子类型替换掉父类型。但简单的说就是能用父类型的地方就一定能使用子类型。里氏替换原则可以检查继承关系是否合理,如果一个继承关系违背了里氏替换原则,那么这个继承关系一定是错误的,需要对代码进行重构。
(5)接口隔离原则:接口要小而专,绝不能大而全。
(6)合成聚合复用原则:优先使用聚合或合成关系复用代码。要说明的是,即使在Java的API中也有不少滥用继承的例子,例如Properties类继承了Hashtable类,Stack类继承了Vector类,这些继承明显就是错误的,更好的做法是在Properties类中放置一个Hashtable类型的成员并且将其键和值都设置为字符串来存储数据,而Stack类的设计也应该是在Stack类中放一个Vector对象来存储数据。记住:任何时候都不要继承工具类,工具是可以拥有并可以使用的,而不是拿来继承的。) 
迪米特法则:迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解。

参考https://blog.csdn.net/gaozongjian/article/details/79485264

32.请问Query接口的list方法和iterate方法有什么区别?

1) list方法无法利用缓存,它对缓存只写不读; iterate方法可以充分利用缓存, 如果目标数据只读或者读取频繁, iterate可以减少性能开销

2) list方法不会引起N+1查询问题, 而iterate方法会引起N+1查询问题

3)对于Query接口的list()方法与iterate()方法来说,都可以实现获取查询的对象,但是list()方法返回的每个对象都是完整的(对象中的每个属性都被表中的字段填充上了),而iterator()方法所返回的对象中仅包含了主键值(标识符),只有当你对iterator中的对象进行操作时,Hibernate才会向数据库再次发送SQL语句来获取该对象的属性值。

参考: http://www.voidcn.com/article/p-pstgslmb-dh.html

http://www.voidcn.com/article/p-eymsmyld-baa.html

33.Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?

重载(Overloading)

(1)方法重载是让类以统一的方法处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数(类型)。重载Override是一个类中多态性的一种表现。

(2)java的方法重载,就是在类中可以创建多个方法,他们具有相同的名字,但具有不同参数和不同的定义。调用方法时通过传递给他们不同的参数个数和参数类型来决定具体使用那个方法,这就是多态性。

(3)重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不同。无法以返回类型来作为重载函数的区分标准。

重写(Overriding)

(1)父类与子类的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写。在java中,子类可继承父类的方法,则不需要重新编写相同的方法。但有时子类并不想原封不动继承父类的方法,而是想做一定的修改,这就采用方法重写。方法重写又称方法覆盖。

(2)若子类中的方法与父类的中的某一方法具有相同的方法名、返回类型和参数表,则新方法覆盖原有的方法。如需要父类的原有方法,可以使用super关键字,该关键字引用房钱类的父类。

(3)子类函数访问权限大于父类

34.Java中,什么是构造函数?什么是构造函数重载?什么是复制构造函数?

当新对象被创建的时候,构造函数会被调用。每一个类都有构造函数。在程序员没有提供构造函数的情况下,Java编译器会为这个类创建一个默认的构造函数。

Java中的构造函数重载和方法重载很类似,可以为一个类创建多个构造函数。每一个构造函数都必须有它自己唯一的参数列表

java不支持像C++中那样的复制构造函数,这个不同点是因为如果你不是自己写的构造函数的情况下,java不会创建默认的复制构造函数。

在C++中

拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:

  • 通过使用另一个同类型的对象来初始化新创建的对象。

  • 复制对象把它作为参数传递给函数。

  • 复制对象,并从函数返回这个对象。

如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。

参考:https://www.cnblogs.com/fanvfan/p/7563372.html

https://blog.csdn.net/weixin_40695212/article/details/79383067

https://www.runoob.com/cplusplus/cpp-copy-constructor.html

35.hashCode()和equals()方法有什么联系?

1.1.1 java基础1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值