校招准备:(一):java基础知识

Table of Contents

1.java关键字与基础:

1.0(只用了解,不用背)java环境配置,clsspath,jre,jdk,包,jar包,war包,import关键字,java编码格式。

包的作用

1.1object有哪些方法?

1.2.static关键字的作用与用法:

1.3final关键字的作用:

1.4equals方法与‘==’运算符有什么区别,如何重载equals方法,为什么修改equal时也要修改hashcode,如何重载hashcode?

1.5如何理解面向对象,范围标识符的作用域,多态的表象形式,重写与重载的区别,Super 关键字的使用:

1.6native关键字,void::

1.7enum类:

1.8java基本类型和引用类型、自动拆装箱,为什么无法修改方法参数中的integer的值?

1.10:jdk1.8新特性,lambda表达式,流式编程。

1.11:java匿名内部类访问局部变量时局部变量必须声明为final。

2.异常:

3.输入输出与nio:


1.java关键字与基础:

1.0(只用了解,不用背)java环境配置,clsspath,jre,jdk,包,jar包,war包,import关键字,java编码格式。

Java环境变量,真的还有必要配吗?JDK(1.8)安装好JDK和JRE,用不着配置什么环境变量就可以开发和跑项目了,在安装的时候就已经自动为我们添加好了环境配置。
Java开发环境不再需要配置classpath!:
当我们配置classpath后,系统会根据我们所配置的classpath加载类

例如:在我们使用javac命令编译程序时,系统加载tools.jar其实就封装了下面这样一条命令
javac XXX.java
java -Classpath=%JAVA_HOME%\lib\tools.jar xx.xxx.Main XXX.java
在JDK1.5之前,是没有办法在当前目录下加载类的(找不到 JDK目录下lib文件夹中的.jar文件),所以我们需要通过配置classpath,但JDK1.5之后,JRE能自动搜索目录下类文件,并且加载dt.jar和tool.jar的类。

环境变量是什么:
JAVA_HOME,JDK安装的位置比如JAVA_HOME=C:\Program Files\Java\jdk1.8.0_91
path:将程序路径包含在PATH当中后,在命令行窗口就可以直接键入它的名字了,而不再需要键入它的全路径,比如上面代码中。比如环境配制好后可以直接在dos窗口使用javac和java两个命令,不需要再进入目录里面。
PATH=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;%PATH%;
CLASSPATH:指向jar包路径。CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar。现在使用java不需要配制了,直接默认是当前目录。classpath是Java运行时环境搜索类和其他资源文件(比如jar\zip等资源)的路径。可以通过JDK工具(比如javac命令、java命令)后面的-classpath 参数设置。(【Java】classpath的理解及其使用方式)

关于JRE和JDK的区别,终于知道他们的区别了

JVM: java virtual machine,java虚拟机 用来解释执行字节码文件(class文件)的。JVM是Java实现跨平台最核心的部分,所有的Java程序会首先被编译为.class的类文件,JVM的主要工作是解释自己的指令集(即字节码)并映射到本地的CPU的指令集或OS的系统调用。Java面对不同操作系统使用不同的虚拟机,依次实现了跨平台。JVM对上层的Java源文件是不关心的,它关心的只是由源文件生成的类文件。

Jre 是java runtime environment, 是java程序的运行环境。既然是运行,当然要包含jvm,也就是大家熟悉的虚拟机啦,还有所有java类库的class文件,都在lib目录下打包成了jar。大家可以自己验证。至于在windows上的虚拟机是哪个文件呢?学过MFC的都知道什么是dll文件吧,那么大家看看jre/bin/client里面是不是有一个jvm.dll呢?那就是虚拟机。

Jdk 是java development kit,是java的开发工具包,里面包含了各种类库和工具。当然也包括了另外一个Jre. 那么为什么要包括另外一个Jre呢?而且jdk/jre/bin同时有client和server两个文件夹下都包含一个jvm.dll。说明是有两个虚拟机的。这一点不知道大家是否注意到了呢?

相信大家都知道jdk的bin下有各种java程序需要用到的命令,与jre的bin目录最明显的区别就是jdk下才有javac,这一点很好理解,因为 jre只是一个运行环境而已。与开发无关,正因为如此,具备开发功能的jdk自己的jre下才会同时有client性质的jvm和server性质的 jvm, 而仅仅作为运行环境的jre下只需要client性质的jvm.dll就够了。

Client模式和Server模式的区别:

Java程序最初是通过解释器对.class文件进行解释执行的,当虚拟机发现某个方法或代码块运行地特别频繁的时候,就会把这些代码认定为热点代码Hot Spot Code(这也是我们使用的虚拟机HotSpot名称的由来)。为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器叫做即时编译器(Just In Time Compiler,即JIT编译器)

解释器和编译器其实和编译器各有优势:

1、当程序需要迅速启动和执行的时候,解释器可以先发挥作用,省去编译的时间,立即执行

2、在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获取更高的执行效率

我们使用的HotSpot中内置了两个JIT编译器,即C1编译器和C2编译器,默认采用的是解释器和一个编辑器配合的方式进行工作。HotSpot在启动的时候会根据自身版本以及宿主机器的硬件性能自动选择运行模式,比如会检测宿主机器是否为服务器、比如J2SE会检测主机是否有至少2个CPU和至少2GB的内存。

1、如果是,则虚拟机会以Server模式运行,该模式与C2编译器共同运行,更注重编译的质量,启动速度慢,但是运行效率高,适合用在服务器环境下,针对生产环境进行了优化

2、如果不是,则虚拟机会以Client模式运行,该模式与C1编译器共同运行,更注重编译的速度,启动速度快,更适合用在客户端的版本下,针对GUI进行了优化

Java 包(package)

包的作用

  • 1、把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。

  • 2、如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。

  • 3、包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。

Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。

jar包是什么?:JAR(Java ARchive,Java 归档)是一种与平台无关的文件格式,可将多个文件合成一个文件。

JAR 文件就是 Java Archive File,顾名思意,它的应用是与 Java 息息相关的,是 Java 的一种文档格式。JAR 文件非常类似 ZIP 文件——准确的说,它就是 ZIP 文件,所以叫它文件包。JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中,包含了一个 META-INF/MANIFEST.MF 文件,这个文件是在生成 JAR 文件的时候自动创建的。

JAR文件是跨平台的,所以不必关心涉及具体平台的问题。除了可以包括声音和图像文件以外,也可以在其中包括类文件。

JAR 文件不仅用于压缩和发布,而且还用于部署和封装库、组件和插件程序,并可被像编译器和 JVM 这样的工具直接使用。在 JAR 中包含特殊的文件,如 manifests 和部署描述符,用来指示工具如何处理特定的 JAR。
用于发布和使用类库 
□ 作为应用程序和扩展的构建单元 
□ 作为组件、applet 或者插件程序的部署单位 
□ 用于打包与组件相关联的辅助资源 

war包:jar包和war包的介绍和区别:war包是Sun提出的一种web应用程序格式,与jar类似,是很多文件的压缩包。war包中的文件按照一定目录结构来组织。根据其根目录下包含有html和jsp文件,或者包含有这两种文件的目录,另外还有WEB-INF目录。通常在WEB-INF目录下含有一个web.xml文件和一个classes目录,web.xml是这个应用的配置文件,而classes目录下则包含编译好的servlet类和jsp,或者servlet所依赖的其他类(如JavaBean)。通常这些所依赖的类也可以打包成jar包放在WEB-INF下的lib目录下。

一个WAR文件就是一个Web应用程序,建立WAR文件,就是把整个Web应用程序(不包括Web应用程序层次结构的根目录)压缩起来,指定一个.war扩展名。

要注意的是,虽然WAR文件和JAR文件的文件格式是一样的,并且都是使用jar命令来创建,但就其应用来说,WAR文件和JAR文件是有根本区别的。JAR文件的目的是把类和相关的资源封装到压缩的归档文件中,而对于WAR文件来说,一个WAR文件代表了一个Web应用程序,它可以包含 Servlet、HTML页面、Java类、图像文件,以及组成Web应用程序的其他资源,而不仅仅是类的归档文件。

import关键字:import作用

import并不导致类的加载。它的存在纯粹是为了方便写代码,让大家可以把别的package的“名字”引入到当前源码文件里直接用。
在Java源码编译器进行编译时,每个名字都会经过解析(resolution)找到其全名(canonical form),例如说String会被编译器识别为java.lang.String。最后生成到Class文件时就只使用全名来引用各种类啊接口啥的了。

等到Class文件交给JVM的时候,JVM根本看不到啥“import语句”。在源码里写全名还是靠import写省略package的名字,从JVM这边看都一样是全名。所以后续处理自然是一模一样的。

java字符类型采用什么方式:
Java采用UTF-16编码作为内码,也就是说在JVM内部,文本是用16位码元序列表示的,常用的文本就是字符(char)和字符串(String)字面常量的内容。而,UTF-16是Unicode字符集的一种编码方案。

Java字符和字符串存在于以下几个地方:

● Java源码文件,*.java,可以是任意字符编码,如GBK,UTF-8

● Class文件,*.class,采用的是一种改进的UTF-8编码(Modified UTF-8)

● JVM,内存中使用UTF-16编码

Java编译器需要正确的读取源码,消除编码差异,然后编译成UTF-8编码的Class文件。比如javac,默认情况下它会取操作系统的编码,可以使用参数-encoding指定源码文件的字符编码。JVM加载Class文件,把其中的字符或字符串转成UTF-16编码序列。

1.1object有哪些方法?

(1)clone方法
保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。
(2)getClass方法
final方法,获得运行时类型。
(3)toString方法
该方法用得比较多,一般子类都有覆盖。
(4)finalize方法
该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。不过肯定是对象回收之前调用。
(5)equals方法
该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。
(6)hashCode方法
该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。
一般必须满足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。
下面这三个方法,会在多线程处有详细讲解。
(7)wait方法
wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生。
a.其他线程调用了该对象的notify方法。
b.其他线程调用了该对象的notifyAll方法。
c.其他线程调用了interrupt中断该线程。
d.时间间隔到了。
此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。
(8)notify方法
该方法唤醒在该对象上等待的某个线程。
(9)notifyAll方法
该方法唤醒在该对象上等待的所有线程

1.2.static关键字的作用与用法

Java中static关键字的作用与用法:
四种用处:
1,2:修饰成员变量和方法,变成静态方法或变量,属于类,存储到方法区。
3.修饰静态代码块,在加载类初始化的时候执行,只执行一次。
4.静态导包。

5.静态内部类

1.3final关键字的作用:

java中final修饰类:用于修饰类、类属性和类方法。
1.final修饰类不可以被继承,但是可以继承其他类,使用方式跟其它类一样。(java中被final修饰的常用类有哪些?:包装类:Boolean,Character,Short,Integer,Long,Float,Double,Byte,Void(八大类型的包装类型加一个void)字符串类:String,StringBuilder,StringBuffer系统类:Math,StrictMath
2.final修饰的变量称为常量,这些变量只能赋值一次(可以先声明再赋值),但可以装类用于计算。final修饰成员变量,固定的不是内存默认值,需要在创建对象前赋值(
不能创建完对象后,再给对象中的final字段赋值),手动赋值,否则报错,必须保证只能赋值一对于值类型,final修饰指的是值不可变,对于引用类型,则是引用不可变,不能再指向其他对象。
推荐阅读:JVM对于声明为final的局部变量(local var)做了哪些性能优化?
3.final修饰的方法,不可以被覆盖(覆盖=重写(重写:子类中的方法与父类中的方法同名,同参数列表,即使返回值不同,仍然是属于重写)),但可以继承使用。

4.final修饰的形参,传递的值不需要是final类型。
在方法参数前面加final关键字就是为了防止数据在方法体中被修改。

下面的代码没有bug:
public class Main {
    public int a;
   public void test(final Main main){
       main.a++;
   }
    public static void main(String[] args) {
       Main main=new Main();
       main.test(main);
       System.out.println(main.a);
    }
}

1.4equals方法与‘==’运算符有什么区别,如何重载equals方法,为什么修改equal时也要修改hashcode,如何重载hashcode?

1.equals比较的是对象的内容,而‘==’比较的是对象的地址,判断他们是不是引用的同一个对象。equals未重载时返回的就是==:

public boolean equals(Object obj) {    
    return (this == obj);    
}

2.重写equals方法需要满足:

自反性。对于任何非null的引用值x,x.equals(x)应返回true。

对称性。对于任何非null的引用值x与y,当且仅当:y.equals(x)返回true时,x.equals(y)才返回true。

传递性。对于任何非null的引用值x、y与z,如果y.equals(x)返回true,y.equals(z)返回true,那么x.equals(z)也应返回true。

一致性。对于任何非null的引用值x与y,假设对象上equals比较中的信息没有被修改,则多次调用x.equals(y)始终返回true或者始终返回false。

实例:string重载equals:先试用==判断是否同对象,然后使用instanceof(Java关键字(一)——instanceof instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例)判断是否是String类。

public boolean equals(Object anObject) {  
    if (this == anObject) {  
        return true;  
    }  
    if (anObject instanceof String) {  
        String anotherString = (String)anObject;  
        int n = count;  
        if (n == anotherString.count) {  
            char v1[] = value;  
            char v2[] = anotherString.value;  
            int i = offset;  
            int j = anotherString.offset;  
            while (n– != 0) {  
                if (v1[i++] != v2[j++])  
                    return false;  
            }  
            return true;  
        }  
    }  
    return false;  
}

Java提高篇——equals()与hashCode()方法详解:

hashcode的要求:HashCode的存在主要是为了查找的快捷性,HashCode是用来在散列存储结构中确定对象的存储地址的

  • 在一个Java应用的执行期间,如果一个对象提供给equals做比较的信息没有被修改的话,该对象多次调用hashCode()方法,该方法必须始终如一返回同一个integer。

  • 如果两个对象根据equals(Object)方法是相等的,那么调用二者各自的hashCode()方法必须产生同一个integer结果。

  • 并不要求根据equals(java.lang.Object)方法不相等的两个对象,调用二者各自的hashCode()方法必须产生不同的integer结果。然而,程序员应该意识到对于不同的对象产生不同的integer结果,有可能会提高hash table的性能。

大量的实践表明,由Object类定义的hashCode()方法对于不同的对象返回不同的integer。
在object类中,hashCode定义如下:是一个本地方法,它的实现是根据本地机器相关的。

public native int hashCode();

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

1、相等(相同)的对象必须具有相等的哈希码(或者散列码)。
2、如果两个对象的hashCode相同,它们并不一定相同。

java 如何重写equal 和hashcode方法(最佳实践):

  1. 把某个非零的常数值,比如17,保存在一个名为resultint类型的变量中。

  2. 对于对象中每个关键域f(equals方法中涉及的每个域),完成以下步骤:

    1. 为该域计算int类型的散列码c:

      1. 如果该域是boolean类型,则计算(f?1:0)。

      2. 如果该域是bytecharshort或者int类型,则计算(int)f

      3. 如果该域是long类型,则计算(int)(f^(f>>>32))

      4. 如果该域是float类型,则计算Float.floatToIntBits(f)

      5. 如果该域是double类型,则计算Double.doubleToLongBits(f),然后按照步骤2.1.3,为得到的long类型值计算散列值。

      6. 如果该域是一个对象引用,并且该类的equals方法通过递归地调用equals的方式来比较这个域,则同样为这个域递归地调用hashCode。如果需要更复杂的比较,则为这个域计算一个范式(canonical representation),然后针对这个范式调用hashCode。如果这个域的值为null,则返回0(其他常数也行)。

      7. 如果该域是一个数组,则要把每一个元素当做单独的域来处理。也就是说,递归地应用上述规则,对每个重要的元素计算一个散列码,然后根据步骤2.2中的做法把这些散列值组合起来。如果数组域中的每个元素都很重要,可以利用发行版本1.5中增加的其中一个Arrays.hashCode方法。

    2. 按照下面的公式,把步骤2.1中计算得到的散列码c合并到result中:result = 31 * result + c; //此处31是个奇素数,并且有个很好的特性,即用移位和减法来代替乘法,可以得到更好的性能:`31*i == (i<<5) - i, 现代JVM能自动完成此优化。

  3. 返回result

  4. 检验并测试该hashCode实现是否符合通用约定。

@Override
   public int hashCode() {
        int result = 17;
        result = 31 * result + mInt;
        result = 31 * result + (mBoolean ? 1 : 0);
        result = 31 * result + Float.floatToIntBits(mFloat);
        result = 31 * result + (int)(mLong ^ (mLong >>> 32));
        long mDoubleTemp = Double.doubleToLongBits(mDouble);
        result =31 * result + (int)(mDoubleTemp ^ (mDoubleTemp >>> 32));
        result = 31 * result + (mString == null ? 0 : mMsgContain.hashCode());
        result = 31 * result + (mObj == null ? 0 : mObj.hashCode());
        return result;
    }

1.5如何理解面向对象,范围标识符的作用域,多态的表象形式,重写与重载的区别,Super 关键字的使用:

1.面向对象是一种思想,把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。一切皆为对象,对象是程序的基本单元,对象把程序与数据封装起来提供对外访问的能力,提高软件的重用性,灵活性和扩展性。

面向对象有三大特征:封装性、继承性、多态性,

2.校招准备:(一):java基础知识_第1张图片

3.Java 多态:多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作,

多态的优点1. 消除类型之间的耦合关系2. 可替换性3. 可扩充性4. 接口性5. 灵活性6. 简化性

多态存在的三个必要条件:1.继承2.重写3.父类引用指向子类对象。比如:Parent p = new Child();

多态的实现方式:1:重写,2:接口,3:抽象类和抽象方法。

4.Java 重写(Override)与重载(Overload):

重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!

重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

方法的重写规则:

  • 参数列表必须完全与被重写方法的相同。

  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。

  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。

  • 父类的成员方法只能被它的子类重写。

  • 声明为 final 的方法不能被重写。

  • 声明为 static 的方法不能被重写,但是能够被再次声明。

  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。

  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。

  • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。

  • 构造方法不能被重写。

  • 如果不能继承一个方法,则不能重写这个方法。

重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

最常用的地方就是构造器的重载。

重载规则:

  • 被重载的方法必须改变参数列表(参数个数或类型不一样);
  • 被重载的方法可以改变返回类型;
  • 被重载的方法可以改变访问修饰符;
  • 被重载的方法可以声明新的或更广的检查异常;
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。

5.当需要在子类中调用父类的被重写方法时,要使用 super 关键字。

1.6native关键字,void::

1.自己实现一个Native方法的调用:被Native关键字声明的方法说明该方法不是以Java语言实现的,而是以本地语言实现的,Java可以直接拿来用。这里有一个概念,就是本地语言,本地语言这四个字,个人理解应该就是可以和操作系统直接交互的语言。

2.Java 中的 Void 和 void:void 是一种基本数据类型。而 Void 是 void 封装类型,API 上称其为“占位符(placeholder)”。

它不可实例化
它没有内置的类型实例,也没有可用的字面值
声明一个 Void 变量通常是没有什么意义的,因为没有可为其赋值的实例,注意不能用 void 为其赋值(void 是类型而不是值)。
Void 变量唯一能持有的是 null。
Void 一个可用的地方是反射判断方法的返回值时基本上没啥用。

1.7enum类:

java枚举类型enum的使用:取代public final static:

 所有的枚举都继承自java.lang.Enum类。因为java单继承,所以不能再继承其他类。

public enum Light {
       // 利用构造函数传参
       RED (1), GREEN (3), YELLOW (2);
       // 定义私有变量
       private int nCode ;
       // 构造函数,枚举类型只能为私有
       private Light( int _nCode) {
           this . nCode = _nCode;
       }
       @Override
       public String toString() {
           return String.valueOf ( this . nCode );
       }
    }

深入理解Java枚举类型(enum):

实现懒加载单例模式:

其他方式的单例模式可能会破坏单例模式,每次反序列化一个序列化的对象实例时都会创建一个新的实例。反射不安全。
而枚举序列化是由jvm保证的,每一个枚举类型和定义的枚举变量在JVM中都是唯一的,且禁止反射。

public enum  SingletonEnum {
    INSTANCE;
    private String name;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
}

1.8java基本类型和引用类型、自动拆装箱,为什么无法修改方法参数中的integer的值?

1:Java的基本类型

Java的基本类型主要分为:整数型:byte,short,int,long; 浮点型:float,double布尔型:boolean字符型:char

基本类型的大小

         1byte:8bit,一个bit代表一个1或者0,是计算机的基本单位。

         byte:1byte        short:2 byte       int:4byte          long:8byte

         float:4byte        double:8个byte

         char:2byte        

         boolean:值只可以为true或者false ,理论上只占据一个bit,但是实际是占据了一个byte

基本类型存储于内存的常量池中,而在1.8以后常量池也又堆中转化到了直接内存的方法区中。

对象本身来说是比较消耗资源的,使用基本数据类型,这种数据的变量不需要使用new创建,他们不会在堆上创建,而是直接在栈内存中存储,因此会更加高效。

引用类型:引用类型变量指向的是地址。对于特定的OS体系结构,地址的大小将是恒定的。在32位的情况下,它将是4个字节,对于64位,它将是8个字节。 因此,指向的对象大小可能会改变,但引用的大小保持不变,因为它们只是携带对于任何特定体系结构保持不变的地址。

2.什么是Java中的自动拆装箱:基本数据对应的包装类:Boolean,Character,Short,Integer,Long,Float,Double,Byte。

自动装箱都是通过包装类的valueOf()方法来实现的.自动拆箱都是通过包装类对象的xxxValue()来实现的。触发场景:1.将基本数据类型放入集合类,2.包装类型和基本类型的大小比较,3.包装类型的运算,4.三目运算符的使用。5.函数参数与返回值。

Java常量池详解之Integer缓存:(只适用于自动装箱。使用构造函数创建对象不适用。)Integer.valueOf()中有个内部类IntegerCache(类似于一个常量数组,也叫对象池),它维护了一个Integer数组cache,长度为(128+127+1)=256;Integer类中还有一个Static Block(静态块)。在块中,Integer已经默认创建了数值【-128-127】的Integer缓存数据,JVM会直接在该在对象池找到该值的引用。

3.Java对于修改Integer变量值的问题:Integer变量里的value是final变量,是不能够修改的,对它赋值5000,相当于new了一个新的Integer变量,它的value值是5000,然后把这个新的变量赋给“shuzi”这个变量,原来那个value值为100的Integer变量就被覆盖了,除非我用一个副本保存,不然他就会被GC清除了。

public void ceshi(Integer integer){
    integer=4444;
}

Java 只有值传递,只不过值传递分为:内存中数值的值传递以及内存地址数值的值传递,传递一个Integer变量参数进去,实际上是构建了一个副本,通过这个副本我们只能去修改原来Integer变量的非final成员变量(假如有的话,也可以是其他类型),上面也说了,如果去修改Integer类型的final变量,那么是会新new一个Integer变量,去覆盖这个变量副本,所以原来的Integer变量还是原来的,仅仅是“ceshi”这个方法里的副本变量变了,这么理解就清楚了。

1.10:jdk1.8新特性,lambda表达式,流式编程。

jdk1.8新特性总结:1.default关键字:使用default修饰方法,可以让我们在接口里面定义具体的方法实现。

public interface NewCharacter {
    
    public void test1();
    
    public default void test2(){
        System.out.println("我是新特性1");
    }
}

这么定义一个方法的主要意义是定义一个默认方法,也就是说这个接口的实现类实现了这个接口之后,不用管这个default修饰的方法,也可以直接调用,

2.lambda表达式
3、函数式接口:仅仅只包含一个抽象方法的接口
4.方法与构造函数引用
5.局部变量限制
6、Date Api更新
7.流

JDK1.8 新特性(全)

Java的Stream流式处理

lambda之Stream流式编程

Java 8 Lambda 异常处理

1.11:java匿名内部类访问局部变量时局部变量必须声明为final

直接点进去看知乎大神的讲解就好。我在下面摘录几条。

java编译器支持了闭包,但支持地不完整。说支持了闭包,是因为编译器编译的时候其实悄悄对函数做了手脚,偷偷把外部环境方法的x和y局部变量,拷贝了一份到匿名内部类里。如下面的代码所示。

interface AnnoInner(){addXYZ();}
public class Outer {
    public AnnoInner getAnnoInner(final int x){
        final int y=100;
        return new AnnoInner(){
            int z=100;
            public int addXYZ(){return x+y+z;}
            //public void changeY(){y+=1;}	//这个函数无法修改外部环境中的自由变量y。
        };
    }

    private int num=100;
}
//编译后相当于下面的代码
interface AnnoInner(){
    addXYZ();
}
public class Outer {    
    public AnnoInner getAnnoInner(final int x){
        final int y=100;
        return new AnnoInner(){
			int copyX=x;	//编译器相当于拷贝了外部自由变量x的一个副本到匿名内部类里。
			int copyY=y;	//编译器相当于拷贝了外部自由变量y的一个副本到匿名内部类里。
            int z=100;
            public int addXYZ(){return x+y+z;}
            //public void changeY(){y+=1;}	//这个函数无法修改外部环境中的自由变量y。
        };
    }
    private int num=100;
}

不止匿名内部类访问需要使用final,还有:1.外部类成员方法内部的内部类。2.在一个代码块block里的内部类。

这么做可以避免:1. 外部方法修改引用,而导致内部类得到的引用值不一致 2.内部类修改引用,而导致外部方法的参数值在修改前和修改后不一致。于是就用 final 来让该引用不可改变。

注意:方法中不能直接定义方法,但可以在方法中定义类,类里面再定义方法。

理论上内部类可以定义在类中的任意位置上,这就包括了:类、方法、代码块中。(https://blog.csdn.net/weixin_41197830/article/details/79643273

java中的匿名内部类总结:匿名内部类也就是没有名字的内部类,正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写,但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口。如果只有一个接口,可以直接转化为lambda表达式。

Java局部变量final

在一个线程A中开起另一个线程B,如果线程B要使用线程A的局部变量,那么A的局部变量需要定义成final。理由:局部变量是线程内部共享的,每一个线程内的不能访问其他线程的局部变量,但是上诉的情况却违背了这一原则,那么加上final为什么就可以了呢?原因是加上final之后,在创建B线程的时候会把final标记的变量作为线程B的构造方法的参数传给B,如此一来就解决了此问题,这是一个比较巧妙的做法,通过class文件反编译可以看出这个道理。

引用外部类的成员变量的时候,不需要final修饰。

1.12Java创建对象的几种方式

创建对象的方式有四种:

  • 用new关键字创建
  • 调用对象的clone方法:
  • 利用反射,调用Class类的或者是Constructor类的newInstance()方法
    Class c = Class.forName(className);
    factory = (ExampleInterface)c.newInstance();
  • 用反序列化,调用ObjectInputStream类的readObject()方法

java实现克隆的三种(很最全面)

深克隆,浅克隆,通过序列化克隆

java中为什么要实现序列化,什么时候实现序列化?

 序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的

声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态, transient代表对象的临时数据。

什么时候使用序列化:
  一:对象序列化可以实现分布式对象。主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。
  二:java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的"深复制",即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。

 

1.13String、StringBuffer与StringBuilder的区别,及实现原理

String使用final关键字修饰可以知道String是不可变的类,String中字符数组的长度你定义多少,就是多少,不存在字符数组扩容一说。内部是final修饰的char[] value,表示String类不可被继承,且value只能被初始化一次。这里的value变量其实就是存储了String字符串中的所有字符。

StringBuffer和StringBuilder二者的源码以及append方法,二者都是AbstractStringBuilder的子类,也都实现了Serializable(可序列化)和CharSequence(char类型的可读序列)接口,AbstractStringBuilder 实现了Appendable(长度可增加)和CharSequence接口。

在参数String类型的构造方法中,在本身字符串长度的基础上再增加16个字符长度,作为StringBuilder实例的初始数组容量,并将str字符串 append到StringBuilder的数组中。
这里的toString方法直接new 一个String对象,将StringBuilder对象的value进行一个拷贝,重新生成一个对象,不直接操作StringBuilder的value。
StringBuilder没有像String一样去重新new 对象,所以在频繁的拼接字符上省去了new 关键字的StringBuilder,不必每次都要重新开辟新的内存空间,其效率远远高于String类。

StringBuffer是线程安全的高效字符串操作类,在StringBuilder上加了锁机制

String 类不可变,内部维护的char[] 数组长度不可变,为final修饰,String类也是final修饰,不存在扩容。字符串拼接,截取,都会生成一个新的对象。频繁操作字符串效率低下,因为每次都会生成新的对象。
StringBuilder 类内部维护可变长度char[] , 初始化数组容量为16存在扩容, 其append拼接字符串方法内部调用System的native方法,进行数组的拷贝,不会重新生成新的StringBuilder对象。
它是非线程安全的字符串操作类, 其每次调用 toString方法而重新生成的String对象,不会共享StringBuilder对象内部的char[],会进行一次char[]的copy操作。
StringBuffer 类内部维护可变长度char[], 基本上与StringBuilder一致,但其为线程安全的字符串操作类,大部分方法都采用了Synchronized关键字修改,以此来实现在多线程下的操作字符串的安全性。
其toString方法而重新生成的String对象,会共享StringBuffer对象中的toStringCache属性(char[]),但是每次的StringBuffer对象修改,都会置null该属性值。

1.14java多进程

启动一个java程序对应一个jvm,那如果奇多多个java程序,是不是创建多个jvm呢?

对于常见的JRE来说,是的。 基本上一个Java程序一个Java虚拟机(一个Java进程)。 某个新版本的JRE开始,貌似开始优化这一状况,多个Java虚拟机可以共享一些内存,貌似。

如果觉得统一机器上起多个Java虚拟机浪费cpu和内存,(比如每个虚拟机都开垃圾回收线程。)可以将多个应用稍作改造运行在一个虚拟机里面。 但是这么做也有缺点,例如一个应用有内存泄漏漏洞,一个应用中写了个死循环,这个虚拟机里的所有应用都不能正常工作

在同一个虚拟机上使用同一个JDK部署了几个JAVA应用是否独立JVM

linux命令:ps -ef|grep java应用名称 ,查看进程信息。我一看就想明白了,每个一个独立的JAVA应用是一个独立的进程,那么也就是说是独立的一个JVM。几个JAVA应用是不会因为使用同一个JAVA_HOME、CLASSPATH就有影响的。因为JAVA应用进程启动的时候就启动了一个独立JVM,所以JVM的参数不一样的。只要你机器的内存、cpu够大,合理分配即可。豁然开朗,只是部署方式不一样而已。

一个tomcat有几个jvm

1、一个tomcat是一个进程,其中有很多线程(与有多少个app无关)

2、一个tomcat启动一个JVM,其中可以有很多APP

3、一个tomcat中部署的多个app,虽然同处一个JVM里,但是由于无法相互调用,所以也可以认为是分布式的

一个tomcat只启动一个JVM,也就是说3个应用都是跑在一个JVM里,之所以它们不能互相调用是因为被类加载器隔离开的。

Tomcat 的类加载器层次是:

Bootstrap
|
System
|
Common
/
Webapp1 Webapp2 ...

每个应用的中的类分别是由Webapp1、Webapp2类加载器加载的,所以是相互不可见的。

关于类加载器可以看看http://unmi.cc/tag/classloader

类加载器的规则有三

1. 一致性规则:类加载器不能多次加载同一个类
2. 委托规则:在加载一个类之前,类加载器总参考父类加载器
3. 可见性规则:类只能看到由其类加载器的委托加载的其他类,委托是类的加载器及其所有父类加载器的递归集

jvm与tomcat的关系

1.15可以修饰类的关键词

abstract,final。

public: Java 语言中类的可访问控制符只有一个: public 即公共的。每个 Java 程序的主类都必须是 public 类作为公共工具供其它类和程序使用的应定义为 public 类。

类缺省访问控制符:如果一个类没有访问控制符,说明它具有缺省的访问控制符特性。此时,这个类只能被同一个包中的类访问或引用。这一访问特性又称为包访问性。

内部类和接口(interface)可以用static以及public、private、protected修饰。

内部interface默认被static关键字修饰。

1.16abstract修饰符、接口和抽象类的区别。

abstract可以修饰接口(可以修饰接口,但是没有必要,也没有用)、类、方法

抽象类可以没有抽象方法,但是如果你的一个类已经声明成了抽象类,即使这个类中没有抽象方法,它也不能再实例化,即不能直接构造一个该类的对象。
如果一个类中有了一个抽象方法,那么这个类必须声明为抽象类,否则编译通不过。

interface中为属性相当于被public static final 修饰,方法相当于被public abstract修饰。因为接口是不能创建对象的,只能用static来使接口中的常量能通过接口名来访问。

接口定义:接口是一个全部由抽象方法组成的集合,里面都是抽象方法和常量,用interface修饰。

接口作用:作为一种规范类的“协议”,规范实现类,增强扩展性。

区别:

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

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

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

4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然

eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。

5. 抽象类中可以包含静态方法,接口中不能包含静态方法

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

7. 一个类可以实现多个接口,但只能继承一个抽象类。

接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用,例如,模板方法设计模式是抽象类的一个典型应用,

1.17this和super的关系

相同点:super()和this()都必须在构造函数第一行进行调用,否则就是错误。(super()和this()不能同时共存)

不同点:super是调用父类对象,this是表示当前对象。

super()和this()是调用构造方法,可以带参数。

super和this可以调用对象的非private修饰的变量和方法。

1.18protected修饰的变量和继承:

protected的member只能在同包或子类中访问到
父子类 不同包,父类中有protected修饰的变量p
在子类中可以通过this或创建自身的对象访问到该变量p,因为这个p被子类所继承
但是子类中不可以通过创建一个父类的对象来访问p,因为此时p不作为子类继承的成员,而是作为父类的成员,
但是父类的成员在其他包中,父类的对象也无法访问到。

关于成员变量的继承问题:

变量是指向基类,调用是基类的成员变量,子类的方法。属性同名的是完全的覆盖,引用是什么类型调用的就是谁的。

对于引用类型没有而对象里面有的(子类添加的成员)肯定不能访问;对于引用类型有对象里有重名的成员,成员方法可以访问子类对象的方法体,而成员变量只能调用引用类型(父类)的。和具体的数据类型无关。

 

2.异常:

校招准备:(一):java基础知识_第2张图片
Error类和Exception类的父类都是throwable类,他们的区别是:
Error类是程序代码无法处理的错误,虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等,比如OutOfMemoryError、ThreadDeath,Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止退出,其表示程序在运行期间出现了十分严重、不可恢复的错误,应用程序只能中止运行。(编译错误不是这个error)
Exception类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
Exception类又分为运行时异常(Runtime Exception)和受检查的异常(Checked Exception ),运行时异常;ArithmaticException,IllegalArgumentException,编译能通过,但是一运行就终止了,程序不会处理运行时异常,出现这类异常,程序会终止。而受检查的异常,要么用try。。。catch捕获,要么用throws字句声明抛出,交给它的父类处理,否则编译不会通过。
常见的异常;ArrayIndexOutOfBoundsException 数组下标越界异常,ArithmaticException 算数异常 如除数为0。NullPointerException 空指针异常;IllegalArgumentException 不合法参数异常。

catch和finally是不可以同时省略的!

3.输入输出与nio:

javanio的多路复用:
非阻塞同步,使用reactor(反射器)模式,在一个线程内,selector轮询注册的channel有新数据就取出来。

 

你可能感兴趣的:(java笔记)