Java总复习(一)——JavaSE基础

Java概述

体系结构

  1. JRE:Java核心类库
  2. JVM:Java虚拟机
  3. JDK:包含JRE和JVM,以及其他Java命令与工具(如java、javadoc、javac等)

Java最大特点

跨平台:Java文件编译后是class文件,class文件符合Java虚拟机规范,每个平台都有一套JVM,class依赖与JVM而不是操作系统

Java数据类型

  • 8种基本数据类型:byte、short、int、long、float、double、char、boolean
  • 引用数据类型:数组、类对象、枚举、注解

区别基本数据类型存储在栈中;引用类型数据存储在堆中,对象的引用存储在栈中

String,StringBuffer,StringBuilder的区别

  1. String是不可变字符串(String不可变的原因是因为String内部的存储字符串字面量的成员变量是final修饰的),StringBuffer、StringBuilder是可变的字符串对象
  2. String、StringBuffer是线程安全的,StringBuilder是线程不安全的

Java传参问题

Java方法的传参——传值(不论是基本类型还是引用类型);传入的都是引用的副本(这里引用并不是一个地址,而是存储在栈中的值),如果方法中将参数指向另一个对象不会影响实参,但对于引用类型,对参数指向的对象进行修改会影响实参

写一个方法将两个Integer变量交换笔试题

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Integer a = new Integer(10);
        Integer b = new Integer(20);
        swap(a,b);
        System.out.println(a);
        System.out.println(b);
    }

    public static void swap(Integer a,Integer b) throws NoSuchFieldException, IllegalAccessException {
        Class c = Integer.class;
        Field field = c.getDeclaredField("value");
        //设置可访问性,成败就在这里,由于value字段是private final,只有这一步设置才可以在外部改变他的值
        field.setAccessible(true);
        int numA = (int) field.get(a);
        field.set(a,field.get(b));
        field.set(b,numA);
    }

包装类

每一个基本数据类型都对应有一个包装类:Byte、Short、Integer、Long、Float、Double、Character、Boolean
自动装箱与拆箱
int i = new Integer(100):拆箱
Integer i = 100:装箱
体现:方法传参、泛型集合
Integer的比较
Integer内部有缓冲区默认范围是-128~127,在范围内的数据都存储在常量池中,创建相同数据时会将同一个值返回;超过这个范围会在堆中创建一个Integer对象,两个相同大小的值对应着两个相同的对象,但在堆中的地址不同;而==对于引用类型比较的是地址

Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1==i2);//true
Integer i1 = 128;
Integer i2 = 128;
System.out.println(i1==i2);//false

变量

  1. 方法中的变量无初始值,使用前要先赋值
  2. 类中的变量为全局变量,引用类型默认为null;基本数据类型默认值int为0,boolean为false;
  3. 代码块中的局部变量默认没有初始值

运算符

  1. 整数的除法运算出现小数会出现精度缺失,如5/2=2
  2. 取余运算结果的符号取决于被除数(前面的数),如5%-2=1;-5%2=-1
  3. 当参与运算中出现精度更高的类型,则结果为该类型,如7/2?1:0.9结果为1.0而不是1
  4. 逻辑运算符的短路,&&当第一个为false,则第二个不会执行;||当第一个为true,则第二个不会执行
  5. 位运算符&,|即两边转换为二进制数对应位进行逻辑运算,如4(100)&6(110)结果为4(100)
    &和&&的区别

&运算的是boolean和数字,&&运算的是boolean
&没有短路,&&会短路

==与equals的区别

equals如果没有Override,默认实现就是==
==可以操作基本类型和引用类型;equals只有引用类型有

OOP

  1. 类与对象:实例化对象的方式——new、反射、克隆、序列化
  2. 构造方法:名字与所在类一致,没有返回值类型
  3. 方法重载overload:同一类中,方法名相同,参数不同(类型、数量、顺序)
  4. 方法重写override:子类继承父类,重写父类相同名称、相同参数方法,子类的返回值可以和父类相同或者返回值是父类的子类子类抛出的异常不能比父类的多子类的访问控制符必须比父类的大

关键字

static

  1. static修饰类时只能修饰内部类,内部类的实例化方式为通过外部类打点找内部类
  2. static修饰方法,该方法属于类,可以通过类名打点直接调用
  3. static修饰类成员变量,只加载一次,在类实例化之后加载,所有对象共享一个成员变量
  4. static修饰代码块,代码块内部只能捕获并处理异常不能抛出异常
  5. 静态导入,通过import static.package.class.method可以将方法进行静态导入,在使用时可以不用写类名直接使用方法,method可以使用通配符*

this

表示当前实例,不能用于静态代码块和静态方法
this()访问本类的其他重载形式的构造方法

final

  1. final修饰的类不能被继承(构造方法私有也不能被继承)
  2. final修饰的方法不能重写
  3. final修饰的变量不可重新赋值,如果修饰的是引用类型的变量内部变量可以修改;如果修饰的是类成员,静态:直接赋值,静态代码块中赋值,非静态:直接赋值,代码块或构造方法赋值

super

表示父类的实例,使用super.来访问父类的属性或方法;super()在子类的构造方法中访问父类的构造方法

extends、implements

Java中不能多继承但能多实现(即一个类不能同时继承多个类,但是能同时实现多个接口),因为如果多个类有相同的方法,在子类调用父类方法时会有歧义,而接口对方法没有实现,需要子类进行实现所以没有歧义,在多实现时对于接口中重名的默认方法,子类必须重写该方法

类的加载

类加载时机

  1. 创建类的实例,即new一个对象
  2. 访问类的静态变量;访问类的静态方法
  3. 访问类的静态方法
  4. 反射,Class.forName
  5. 加载该类的子类(会首先加载子类的父类)
  6. 虚拟机启动时,定义了main()方法的类

类初始化顺序

父类静态代码块——》子类静态代码块——》父类变量——》父类代码块——》父类构造方法——》子类变量——》子类代码块——》子类构造方法

单例模式

保证所有用到该类的实例的地方用到的都是同一个实例

  1. 饿汉式
public class Singleton{
	//防止外部调用构造方法来构造实例
	private Singleton(){}
	//在类加载时进行实例化
	private static Singleton sinleton = new Sinleton();
	//获取实例化的对象
	public static Singleton getInstance(){
		return sinleton;
	}
}

该方式时线程安全的,但是如果有其他静态成员,可能不需要实例化时却实例化了,浪费资源
2. 懒汉式(线程不安全)

public class Sinleton{
	private Sinleton(){}
	private static Sinleton sinleton = null;
	public static Sinleton getInstance(){
		if(sinleton == null){
			sinleton = new Sinleton();
		}
		return sinleton;
	}
}

该方法只有在需要使用这个实例时实例化,但是存在线程安全问题
3. 懒汉式(线程安全,使用synchronized关键字)

public class Sinleton{
	private Sinleton(){}
	private static Sinleton sinleton = null;
	public static synchronized Sinleton getInstance(){
		if(sinleton == null){
			sinleton = new Sinleton();
		}
		return sinleton;
	}
}
  1. 静态内部类
public class Singleton {
	private Singleton () {}
	//利用静态内部类创建实例来保证实例只创建一次
    private static class SingletonHolder{
       private static final Singleton singleton= new Singleton();
    }
    
    public static final Singleton getInstance () {
        return SingletonHolder.singleton;
    }
 }

面向对象的特征(经典题目)

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

  1. 封装:仅暴露可供调用的接口,将内部实现细节隐藏起来,Java通过访问控制修饰符来进行封装
  2. 继承:子类可以保有且复用父类的属性或方法 ,除了私有成员、构造方法,Java通过extends关键字来实现继承
  3. 多态:一种写法可以在不同的场景下有不同的意义,Java通过使用接口或者父类作为参数或者返回值,在不同场景下有不同的子类实现

异常

类层次结构

Java总复习(一)——JavaSE基础_第1张图片

异常常见面试题

  1. 受检异常和运行时异常的区别:受检异常必须显示处理,运行时异常不处理也能运行
  2. try-catch捕获异常时,可以捕获父类异常
  3. 只要执行了try-catch,不论什么情况都会执行finally; 如果try、catch、finally中都有return,finally中的代码就会在try和catch之前执行,会执行finally中的return而不会执行try、catch中的,如代码所示:
public static void main(String[] args) throws ClassNotFoundException {
        System.out.println(test());
    }

    public static int test(){
        try{
            int i = 1/1;
            return 1;
        }catch (Exception e){
            return 0;
        }finally {
            return -1;
        }
    }

输出结果为-1而不是1

public static void main(String[] args) throws ClassNotFoundException {
        System.out.println(test());
    }

    public static int test(){
        try{
            int i = 1/0;
            return 1;
        }catch (Exception e){
            return 0;
        }finally {
            return -1;
        }
    }

输出结果时0而不是1

  1. 常见的异常有:NullPointException、IOException、ArrayIndexOutOfBoundsException、PersistenceException、ClassNotFoundException
  2. 自定义异常继承自运行时异常,实现四个构造方法,分别时无参、message、throwable、message+throwable;作用:前端可以针对不同的异常做出不同的响应、事务可以根据异常进行回滚

集合

层次结构

Java总复习(一)——JavaSE基础_第2张图片

List和Set的区别

  • List:保证有序(增加进去的顺序),可重复,在查找操作频繁时效率更高
  • Set:不保证顺序,不可重复(HashSet通过hashCode和equals来保证不重复,TreeSet通过compareTo方法来保证不重复),在集中删除或修改时效率更高

ArrayList、LinkedList、Vector的区别

  • ArrayList和Vector内部实现相同,使用数组存储数据,检索更快,ArrayList线程不安全,Vector线程安全(通过synchronized关键字)
  • LinkedList:存储方式是双向链表,增加删除更快,LinkedList线程不安全

集合线程安全问题

  1. 在多线程环境下,哪些集合是可用的
    Vector,Collection,JUC
  2. 线程安全是什么意思
    多个线程同时操作集合的实例时,会导致数据出现不一致的问题

Map

  1. HashMap和HashTable两个实现的功能基本相同,区别在于HashMap是线程不安全的,HashTable是线程安全的
  2. 排序的Map:TreeMap

集合的遍历

  1. 对于List和Set集合,可用通过forEach、for-i、Iterator来遍历(Collection、数组、任何继承了Iterable接口的集合都能够使用迭代器Iterator)
  2. 对于Map集合,可通过keySet()方法来获取键集合,然后通过遍历键来遍历值;通过values()方法来获取值集合进行遍历;通过Map.Entry来进行forEach遍历(Map.Entry entry:map.entrySet())

将List中的元素去重

  1. 将元素存入HashSet中,利用Set不重复特性
List list = new ArrayList();
list.add("a");
list.add("a");
list.add("b");
HashSet set = new HashSet(list);
//清空list
list.clear();
list.addAll(set);
  1. 利用contains方法和一个新的list进行判断
List list = new ArrayList();
list.add("a");
list.add("a");
list.add("b");
List newList = new ArrayList();
list.forEach(s->{
	 //当新list集合中没有该元素时,将该元素存入
     if (!newList.contains(s)){
     	 newList.add(s);
		}
});
  1. 双重循环遍历list,通过equals方法比较去重

IO流

字节流与字符流

输入流

  1. 字节输入流:InputStream、FileInputStream等
  2. 字符输入流:Reader、InputStreamReader等

输出流

  1. 字节输出流:OutputStream、FileOutputStream等
  2. 字符输出流:Writer、OutputStreamWriter等

进行文件拷贝

public static void main(String[] args) throws IOException {
        FileInputStream inputStream = new FileInputStream("D:/1.png");
        OutputStream outputStream = new FileOutputStream("D:/2.png");
        byte []bytes = new byte[50];
        while(inputStream.read(bytes)!=-1){
            outputStream.write(bytes);
        }
    }

网络编程

  1. 服务器端:ServerSocket;客户端:Socket

BIO、NIO、AIO

  1. BIO
    同步阻塞I/O,一个服务器连接对应一个线程,及客户端有连接请求时就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可用通过线程池机制来改善,BIO方式使用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中。

  2. NIO
    同步非阻塞I/O,服务器实现模式为一个请求一个线程,即客户端发送的连接请求会注册到多路复用器上,多路复用器轮询到连接有IO请求时才会启动一个线程进行处理,NIO方式适用于连接数目较多且连接比较多的架构(redis单线程却高性能也是因为底层使用到多路复用),比如聊天服务器,并发局限于应用中,编程复杂。典型的使用NIO的网络编程框架Netty
    多路复用:
    Java总复习(一)——JavaSE基础_第3张图片

  3. AIO
    异步非阻塞I/O,服务器实现模式为一个有效请求一个线程,客户端的IO请求都是由操作系统先完成了再通知服务器用其启动线程进行处理,AIO方式适用于连接数目较多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程复杂

克隆

clone()方法是Object的方法,使用protect修饰,如果子类需要使用到该方法需要重写该方法,修改访问控制符为public,且实现Cloneable接口(类似序列化);克隆的对象与原对像是两个个体,互不相干

浅克隆与深克隆

class Klass{
    private String className;
    public Klass(String className){
        this.className = className;
    }
    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    @Override
    public String toString() {
        return "Klass{" +
                "className='" + className + '\'' +
                '}';
    }
}
class Student implements Cloneable{
    private String name;
    private Klass klass;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Klass getKlass(){
        return klass;
    }
    public void setKlass(Klass klass){
        this.klass = klass;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", klass=" + klass +
                '}';
    }
}

class Scratch{
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student1 = new Student();
        student1.setName("tom");
        student1.setKlass(new Klass("一班"));
        Student student2 = (Student) student1.clone();
        System.out.println(student1);
        System.out.println(student2);
        student2.setName("jack");
        student2.getKlass().setClassName("二班");
        System.out.println(student1);
        System.out.println(student2);
    }
}

结果:

Student{name='tom', klass=Klass{className='一班'}}
Student{name='tom', klass=Klass{className='一班'}}
Student{name='tom', klass=Klass{className='二班'}}
Student{name='jack', klass=Klass{className='二班'}}

这里属于浅克隆,Student实现了Cloneable接口,所以Student的字段能够被克隆出一份新的、与原来对象不相干的数据,而Klass的字段还是共用的同一个数据,所以需要将Klass的字段也实现Cloneable接口,重写Object克隆方法,并在克隆时单独对Klass进行克隆,如下修改Student的clone方法

class Klass implements Cloneable{
    private String className;
    public Klass(String className){
        this.className = className;
    }
    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public String toString() {
        return "Klass{" +
                "className='" + className + '\'' +
                '}';
    }
}
class Student implements Cloneable{
    private String name;
    private Klass klass;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Klass getKlass(){
        return klass;
    }
    public void setKlass(Klass klass){
        this.klass = klass;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        student.setKlass((Klass) student.getKlass().clone());
        return student;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", klass=" + klass +
                '}';
    }
}

class Scratch{
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student1 = new Student();
        student1.setName("tom");
        student1.setKlass(new Klass("一班"));
        Student student2 = (Student) student1.clone();
        System.out.println(student1);
        System.out.println(student2);
        student2.setName("jack");
        student2.getKlass().setClassName("二班");
        System.out.println(student1);
        System.out.println(student2);
    }
}

枚举

  • 用于有限个实例的类的编写
  • 构造方法默认私有且必须是私有的
  • 条件结构switch-case中经常使用
  • enum关键字是语法层面的,它声明的每一个枚举类型都是Enum的子类

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