java基础

113 阅读15分钟

java在DOS中编译和运行

  • 带包编译:文件根目录 javac 文件名.java
    • 例子:javac HelloWorld.java
  • 带包运行:包根目录 java 包名.类名 参数1 参数2
    • 例子:java com.sean.test.HelloWorld arg1 arg2
  • jar包运行: java -jar jar包目录\jar包名称.jar 参数1 参数2
    • 例子:java -jar E:\jar\util.jar arg1 arg2

JDK和JRE区别

JDK包含JRE,JRE包含了jvm和基础的jar,但是JDK包含了更多的工具,例如jconsole,jvisualvm等工具软件,以及java程序员编写的所需的文档

关键字

  • 不常用字段解释
    • strictfp:在接口和类上加上该标识,会对float和double进行严格计算
    • transient:在对象成员变量中加上该标识,在对象序列化时,被标识变量不会被序列化
    • volatile:在变量前加入该标示,解决多线程情况下变量的可见性和有序性,但无法解决原子性问题,应用场景为,变量的值不参与修改的情况。参见:Java并发编程:volatile关键字解析
    • finalize :Object的方法,非关键字,作用是垃圾回收前调用

this

作用:

  • 解决了局部变量隐藏成员变量的问题

static关键字

  1. 特点: 1.1 随着类的加载而加载 1.2 优先于对象存在 1.3 被类的所有对象共享 1.4 既可以通过类名调用,也可以通过对象名调用

1.5 静态方法能够继承但不能重写

  1. 内存图 静态的内容在方法区的静态区

jdk1.8,静态变量和静态方法放在堆中,类的Class对象存储在堆中,类的元信息(class文件内容)存储在元空间中

  1. 注意事项 3.1 静态方法中没有this对象 3.2 静态只能访问静态

final关键字

(1)是最终的意思,可以修饰类,方法,变量。
(2)特点:
    A:它修饰的类,不能被继承。
    B:它修饰的方法,不能被重写。
    C:它修饰的变量,是一个常量。
(3)面试相关:
    A:局部变量
        a:基本类型 值不能发生改变
        b:引用类型 地址值不能发生改变,但是对象的内容是可以改变的
    B:初始化时机
        a:只能初始化一次。
        b:常见的给值
            定义的时候。(推荐)
            或者静态代码块中。
            构造方法中。

注意事项:

类名前
    不使用:
        privateprotectedstatic
    使用:
        权限修饰符:默认修饰符,public
        状态修饰符:final
        抽象修饰符:abstract
 成员变量
     不使用:
        abstract
     使用 
        权限修饰符:private,默认的,protectedpublic
        状态修饰符:staticfinal

数据类型

  • 基本数据类型

      A:整数			占用字节数
      	byte			1
      	short			2
      	int 			4
      	long			8
      B:浮点数
      	float			4
      	double			8
      C:字符
      	char			2
      D:布尔
      	boolean			1
    
	注意:!!!!!
		整数默认是int类型,浮点数默认是double。
		
		长整数要加L或者l。
		单精度的浮点数要加F或者f。

思考题和面试题

  • 下面两种方式有区别吗?

    float f1 = 12.345f; // 本身是一个float类型 float f2 = (float)12.345; // 开始是一个double类型,强转为float类型

  • 下面的程序有问题吗,如果有,在哪里呢?

    byte b1 = 3; byte b2 = 4; byte b3 = b1 + b2; // 有问题,byte参与运算就转换为int类型,编译会报错 byte b4 = 3 + 4; // 常量,先把结果计算出来,然后看是否在byte的范围内,如果在,就不报错

  • 下面的操作结果是什么呢?

    byte b = 130; // 编译报错,130不在范围内 byte b = (byte)130; // -126 源码反码补码!???

  • 字符参与运算

    是查找ASCII里面的值 'a' 97 'A' 65 '0' 48

    	System.out.println('a');	// a
    	System.out.println('a' + 1);	// 98
    
  • 字符串参与运算

    这里其实是字符串的连接

    	System.out.println("hello"+'a'+1);	// helloa1
    	System.out.println('a'+1+"hello");	// 98hello
    	System.out.println("5+5="+5+5);		// 5+5=55
    	System.out.println(5+5+"=5+5");		// 10=5+5
    

字符串

面试题1:

​ String s = new String("hello");和String s = "hello"的区别? ​ 前者有两个地址值,一个是在方法区的常量的地址值,另一个是new String()的地址值,有两个对象 ​ 后者是直接赋值,到方法区去找他的地址只有一个对象

面试题2:

​ String s1 = "hello"; ​ String s2 = "world"; ​ String s3 = "helloworld"; ​ System.out.println(s3 == s1 + s2);// false s1+s2会在堆中新建一个地址,地址值不相同(+会重新new一个对象) ​ System.out.println(s3.equals((s1 + s2)));// true

    System.out.println(s3 == "hello" + "world");// false 这个我们错了,应该是true,原因是像 String s3 = "hello" + ",world" 这样直接两个字面值相加的,java文件在编译期间就已经将这条语句做了优化,将其直接变成 "hello,world",等到运行的时候就查找字符串常量池,因此 s3 == s4 返回的结果就为 true。
    System.out.println(s3.equals("hello" + "world"));// true
面试题3:

String,StringBuffer,StringBuilder的区别 String: 内容是不可变的 StringBuffer: 是一个容器,内容是可变的,同步的,线程安全,效率低 StringBuilder: 也是一个容器,内容是可变的,不同步的,线程不安全,效率高

面试题4:

StringBuffer是如何实现线程安全的: 除构造方法外和部分insert和indexOf方法外,都使用了在方法上加同步锁的方式保证线程安全,而未加锁的insert和indexOf方法是通过调用父类,父类再调用其本身的已加锁的方法保证线程安全。


Integer

面试题1:

​ -128到127之间的数据缓冲池问题 ​ 但特殊和要注意的是: 若传入的是 -128~127 的值,则不是引用类型,而是类似 ​ 于基本类型的常量池中的值,故而可以用 == 号去判断 ​ 即: new Integer(127)==new Integer(127); //true

运算符

赋值运算符

扩展的赋值运算符的特点 隐含了自动强制转换。

面试题:

short s = 1;
s = s + 1;(编译错误)

short s = 1;
s += 1;(<==>s=(short)(s+1))
// 请问上面的代码哪个有问题?

位运算

位运算符说明
>>右移运算符,符号左侧数值 按位右移 符号右侧数值指定的位数,若为正数则高位补0,若为负数则高位补1
<<左移运算符,符号左侧数值 按位左移 符号右侧数值指定的位数,并在低位处补0
>>>无符号右移运算符,符号左侧数值 按位右移 符号右侧数值指定的位数,无论正负高位补0
&与(AND)运算符,对两个整型操作数中对应位执行布尔代数,两个位都为1时输出1,否则0
|或(OR)运算符,对两个整型操作数中对应位执行布尔代数,两个位中只要有一个为1就输出1,否则为0
异或(XOR)运算符,对两个整型操作数中对应位执行布尔代数,两个位相等则为0,不相等则为1
~非(NOT)运算符,按位取反运算符翻转操作数的每一位,即0变成1,1变成0

方法

java内存分配

  • 栈:存储局部变量(在方法定义中或者方法申明上定义的变量)以及常量值 数据使用完毕就消失
  • 堆:存储所有new出来的对象 每一个new出来的东西都有地址 每一个变量都有默认值 byte,short,int,long 0 float,double 0.0 char '\u0000' boolean false 引用类型 null 数据使用完毕后,在垃圾回收器空闲的时候回收。
  • 方法区: class内容区域:文件中的内容,包含成员变量和成员方法,每个class的方法会有一个地址,为堆中的调用提供连接 静态区:静态变量和静态方法 这里写图片描述
  • 本地方法区:与本地系统相关
  • 寄存器:CPU相关

创建对象,内存的流程图: (1)把Student.class文件加载到内存 (2)在栈内存为s开辟空间 (3)在堆内存为学生对象申请空间 (4)给学生的成员变量进行默认初始化。null,0 (5)给学生的成员变量进行显示初始化。林青霞,27 (6)通过构造方法给成员变量进行初始化。刘意,30 (7)对象构造完毕,把地址赋值给s变量 这里写图片描述


成员变量和局部变量区别

  1. 在类中的位置 成员变量:类中,方法外 局部变量:在方法中或方法声明上
  2. 在内存中的位置 成员变量:堆 局部变量:栈
  3. 初始化的值 成员变量:有默认值 局部变量:没有默认值,只有定义,赋值,才能使用
  4. 生命周期 成员变量:随着对象的创建而创建,随着对象的消失而消失 局部变量:随着方法的调用而存在,随着方法的结束而消失

构造方法

  • 格式:
    • 方法名和类名相同
    • 没有返回值类型,void也没有
    • 没有返回值
		思考题:构造方法中可不可以有return语句呢?		*****
		可以。而是我们写成这个样子就OK了:return;
		其实,在任何的void类型的方法的最后你都可以写上:return;

代码块执行顺序

看程序写结果:

    A:一个类的静态代码块,构造代码块,构造方法的执行流程
        静态代码块 > 构造代码块 = 显示初始化 (看顺序) > 构造方法
    B:静态的内容是随着类的加载而加载
        静态代码块的内容会优先执行
    C:子类初始化之前先会进行父类的初始化   
注: 类成员变量的初始化顺序:显式初始化与构造代码块的初始化等级一致,故由代码顺序决定初始化顺序,但注意的是构造代码块不能加数据类型

    
结果是:
    静态代码块Fu
    静态代码块Zi
    构造代码块Fu
    构造方法Fu
    构造代码块Zi
    构造方法Zi
class Fu {
    static {
        System.out.println("静态代码块Fu");
    }

    {
        System.out.println("构造代码块Fu");
    }

    public Fu() {
        System.out.println("构造方法Fu");
    }
}

class Zi extends Fu {
    static {
        System.out.println("静态代码块Zi");
    }

    {
        System.out.println("构造代码块Zi");
    }

    public Zi() {
        System.out.println("构造方法Zi");
    }
}

class ExtendsTest2 {
    public static void main(String[] args) {
        Zi z = new Zi();
    }
}

方法重写和方法重载的区别

  • 方法重写:方法名,参数和返回值都相同
  • 方法重载:方法名相同,参数不同,与返回值无关

继承

继承的好处

​ A:提高了代码的复用性 ​ B:提高了代码的维护性 ​ C:让类与类产生了一个关系,是多态的前提

继承的弊端

​ A:让类的耦合性增强。这样某个类的改变,就会影响其他和该类相关的类。 ​ 原则:低耦合,高内聚。 ​ 耦合:类与类的关系 ​ 内聚:自己完成某件事情的能力 ​ B:打破了封装性

继承的成员关系

​ A:成员变量 ​ a:子类的成员变量名称和父类中的成员变量名称不一样,这个太简单 ​ b:子类的成员变量名称和父类中的成员变量名称一样,这个怎么访问呢? ​ 子类的方法访问变量的查找顺序: ​ 在子类方法的局部范围找,有就使用。 ​ 在子类的成员范围找,有就使用。 ​ 在父类的成员范围找,有就使用。 ​ 找不到,就报错。 ​ B:构造方法 ​ a:子类的构造方法默认会去访问父类的无参构造方法 ​ 是为了子类访问父类数据的初始化 ​ b:父类中如果没有无参构造方法,怎么办? ​ 子类通过super去明确调用带参构造 ​ 子类通过this调用本身的其他构造,但是一定会有一个去访问了父类的构造 ​ 让父类提供无参构造 ​

    注意事项:
this(...)或者super(...)必须出现在第一条语句上,否则会出现编译错误。
如果不是放在第一条语句上,就可能对父类的数据进行了多次初始化,所以必须放在第一条语句上。

多态

多态中的成员访问特点

    A:成员变量
        编译看左边,运行看左边
    B:构造方法
        子类的构造都会默认访问父类构造
    C:成员方法
        编译看左边,运行看右边
    D:静态方法
        编译看左边,运行看左边
        (静态和类相关,算不上重写,所以,访问还是左边的)

多态继承中的内存图解

这里写图片描述

多态中的对象变化内存图解

这里写图片描述

面试题

class A {
    public void show() {
        show2();
    }
    public void show2() {
        System.out.println("我");
    }
}
class B extends A {
    /*
    public void show() {
        show2();
    }
    */

    public void show2() {
        System.out.println("爱");
    }
}
class C extends B {
    public void show() {
        super.show();
    }
    public void show2() {
        System.out.println("你");
    }
}
public class DuoTaiTest4 {
    public static void main(String[] args) {
        A a = new B();
        a.show();// 爱
        
        B b = new C();
        b.show();// 你
    }
}

抽象类

抽象类的特点

    A:抽象类和抽象方法必须用关键字abstract修饰
    B:抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类
    C:抽象类不能实例化
    D:抽象类的子类
        a:是一个抽象类。
        b:是一个具体类。这个类必须重写抽象类中的所有抽象方法。

抽象类的问题

    A:抽象类有构造方法,不能实例化,那么构造方法有什么用?
        用于子类访问父类数据的初始化
    B:一个类如果没有抽象方法,却定义为了抽象类,有什么用?    
        为了不让创建对象
    C:abstract不能和哪些关键字共存
        a:final    冲突
        b:private 冲突
        c:static 无意义

抽象类与接口的区别

  1. 抽象类与子类是 is 的关系,接口是 like 的关系,抽象类耦合度更高,有更好的复用性和维护性,接口反之
  2. 抽象类只能被单继承,而接口,子类可以实现多个
  3. 抽象类可以有非常量的成员变量,接口只能有常量的成员变量,接口在1.8后有默认方法,抽象类具有成员方法

接口

接口的成员特点

    A:成员变量
        只能是常量
        默认修饰符:public static final
    B:构造方法
        没有构造方法
    C:成员方法
        只能是抽象的
        默认修饰符:public abstract

类与类,类与接口,接口与接口

    A:类与类
        继承关系,只能单继承,可以多层继承
    B:类与接口
        实现关系,可以单实现,也可以多实现。
        还可以在继承一个类的同时,实现多个接口
    C:接口与接口
        继承关系,可以单继承,也可以多继承

内部类

内部类访问规则

A:可以直接访问外部类的成员,包括私有 B:外部类要想访问内部类成员,必须创建对象

成员内部类

  • 成员内部类不是静态的: 外部类名.内部类名 对象名 = new 外部类名.new 内部类名();
  • 成员内部类是静态的: 外部类名.内部类名 对象名 = new 外部类名.内部类名();
30,20,10
        
        class Outer {
            public int num = 10;
            
            class Inner {
                public int num = 20;
                
                public viod show() {
                    int num  = 30;
                    
                    System.out.println(num);
                    System.out.println(this.num);
                    System.out.println(Outer.this.num);
                }
            }
        }

局部内部类

A:局部内部类访问局部变量必须加final修饰。
B:为什么呢?
    因为局部变量使用完毕就消失,而堆内存的数据并不会立即消失。
    所以,堆内存还是用该变量,而该变量已经没有了。
    为了让该值还存在,就加final修饰。
C:匿名内部类是局部内部类的简化形式
D:匿名内部类的面试题(补齐代码)
        interface Inter {
            void show();
        }
        
        class Outer {
            //补齐代码
            public static Inter method() {
                return new Inter() {
                    public void show() {
                        System.out.println("HelloWorld");
                    }    
                };
            }
        }
        
        class OuterDemo {
            public static void main(String[] args) {
                Outer.method().show(); //"HelloWorld"
            }
        }


复制

浅复制:

将基本数据类型的值进行复制和引用类型的地址进行复制

深复制:

将基本类型的值和引用类型的值进行复制

异常

Error和Exception区别

error一般为jvm的错误,如系统奔溃,虚拟机出错误

运行时异常和非运行时异常有何区别

运行时异常为非检查异常,一般在程序运行时产生的异常,而非运行时异常会有编译错误,需要进行抛出或try,catche处理

列举5个非运行时异常

空指针异常

数组越界异常

字符串越界异常

类型转换异常

数字计算异常

反射

反射的用途

  1. 在运行时,根据类型创建对象,调用方法,对其赋值,在spring的依赖注入中用到
  2. IDE的代码补齐以及查看类的成员变量和成员方法

序列号

什么是序列化和反序列化

序列化是java对象转换为字节数组的过程

反序列化是字节数组转换为java对象的过程

Serializable 和 SerializableID的作用

Serializable 接口若不实现,在调用 ObjectOutputStream#writeObject和ObjectInputStream#readObject这两个方法时,会抛出异常 NotSerializableException

SerializableID的作用是序列化时作为一个对象的标识,若没有该标识,则会根据序列化的字段自动生成一个SerializableID的值,后续进行反序列化时,若修改了成员变量,该值改变,则无法反序列化成功,若定义了一个常量值,即使修改成员变量,依然可以反序列化成功