原博客地址
https://blog.csdn.net/ThinkWon/article/details/104390612?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param#String_1444

谈一下Java的数据类型

  • 基本数据类型
    boolean - 布尔类型
    doubler - 双精度浮点类型 float - 单精度浮点类型
    byte - 字节 char - 字符
    long - 长整型 short - 短整型 int - 整形
  • 引用数据类型
    类 接口 数组
    图片说明

String类能被继承吗,为什么?

不能。在Java中,只要是被定义为final的类,也可以说是被final修饰的类,就是不能被继承的

Java是单线程还是多线程?

Java 语言是多线程的
Java 语言支持多个线程的同时执行,并提供多线程之间的同步机制(关键字为 synchronized)。
首先线程必须由Tread类或者其子类来创建,创建线程的方法有两种:
1.使用类型结构为Thread(Runnable) 的构造子类将一个实现了 Runnable 接口的对象包装成一个线程。
2.从 Thread 类派生出子类并重写 run 方法,使用该子类创建的对象即为线程;
Thread 类已经实现了 Runnable 接口,因此,任何一个线程均有它的 run 方法,而 run 方法中包含了线程所要运行的代码。线程的活动由一组方法来控制。

String,Stringbuffer,StringBuilder的区别?

图片说明

JVM JRE JDK 的关系

  • JDK
    JDK 其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)
  • JRE
    Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包
    如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
  • JVM
    Java Virtual Machine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。
    图片说明

什么是跨平台性?原理是什么

所谓跨平台性,是指java语言编写的程序,一次编译后,可以在多个系统平台上运行。

实现原理:Java程序是通过java虚拟机在系统平台上运行的,只要该系统可以安装相应的java虚拟机,该系统就可以运行java程序。

访问修饰符

图片说明
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
public : 对所有类可见。使用对象:类、接口、变量、方法

&和&&的区别

& 两边都会执行 && 左边出现false就不会执行右边

final

用于修饰类、属性和方法;

  • 被final修饰的类不可以被继承
  • 被final修饰的方法不可以被重写
  • 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的

final finally finalize区别

  • final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表
    示该变量是一个常量不能被重新赋值。
  • finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块
    中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
  • finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调
    用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的
    最后判断。

break ,continue ,return 的区别及作用

  • break 结束当前的循环体
  • Continue 结束正在执行的循环 进入下一个循环条件
  • retern 结束当前的方法 直接返回

    如何跳出当前的多重嵌套循环

    在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。例如:
    public static void main(String[] args) {
      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;
              }
          }
      }
    }
    

重载重写的区别

方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分

重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写。

== 和 equals 的区别是什么

== 判断两个对象的地址值是否相等,可以理解为两个对象是否是同一个对象。
基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址

equals 判断两个对象是否相等

JDK 中常用的包有哪些

java.lang:这个是系统的基础类;
java.io:这里面是所有输入输出有关的类,比如文件操作等;
java.nio:为了完善 io 包中的功能,提高 io 包中性能而写的一个新包;
java.net:这里面是与网络有关的类;
java.util:这个是系统辅助类,特别是集合类;
java.sql:这个是数据库操作的类。

BIO,NIO,AIO 有什么区别?

简答

  • BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
  • NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
  • AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。

详细回答

  • BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
  • NIO (New I/O): NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发
  • AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。

什么是反射机制

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

Java获取反射的三种方法

1.通过new对象实现反射机制 2.通过路径实现反射机制 3.通过类名实现反射机制

 public class Student {
    private int id;
    String name;
    protected boolean sex;
    public float score;
    }
public class Get {
    //获取反射机制三种方式
    public static void main(String[] args) throws ClassNotFoundException {
        //方式一(通过建立对象)
        Student stu = new Student();
        Class classobj1 = stu.getClass();
        System.out.println(classobj1.getName());
        //方式二(所在通过路径-相对路径)
        Class classobj2 = Class.forName("fanshe.Student");
        System.out.println(classobj2.getName());
        //方式三(通过类名)
        Class classobj3 = Student.class;
        System.out.println(classobj3.getName());
    }
}

String

String 是最基本的数据类型吗

不是。Java 中的基本数据类型只有 8 个 :byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(referencetype),Java 5 以后引入的枚举类型也算是一种比较特殊的引用类型。

String有哪些特性

  • 不变性:String 是只读字符串,是一个典型的 immutable 对象,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象
  • 常量池优化:String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。
  • final:使用 final 来定义 String 类,表示 String 类不能被继承,提高了系统的安全性。

    String为什么是不可变的吗?

    简单来说就是String类利用了final修饰的char类型数组存储字符,源码如下图所以
    private final char value[];
    String 类是 final 类,不可以被继承
    String不可变但不代表引用不可以变
    String str = "Hello";
    str = str + " World";
    System.out.println("str=" + str);
    //输出结果 str=Hellow world;

通过反射是可以修改所谓的“不可变”对象

// 创建字符串"Hello World", 并赋给引用s
String s = "Hello World";

System.out.println("s = " + s); // Hello World

// 获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");

// 改变value属性的访问权限
valueFieldOfString.setAccessible(true);

// 获取s对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s);

// 改变value所引用的数组中的第5个字符
value[5] = '_';

System.out.println("s = " + s); // Hello_World

/* 输出结果
s = Hello World
s = Hello_World*/

数组有没有 length()方法?String 有没有 length()方法

数组没有 length()方法 ,有 length 的属性。String 有 length()方法。JavaScript中,获得字符串的长度是通过 length 属性得到的,这一点容易和 Java 混淆。

String 类的常用方法都有那些?

indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。

String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的

可变性

String类中使用字符数组保存字符串,private final char value[],所以string对象是不可变的。StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。

线程安全性

String中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

性能

每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

多线程

线程

线程被称为轻量级进程,是程序执行的最小单位,它是指在程序执行过程中,能够执行代码的一个执行单位。每个程序程序都至少有一个线程,也即是程序本身
进程中拥有独立功能的执行单元,一般用于完成某种操作

线程的六种状态

.线程的六种状态
  • 新建状态(New)

    刚刚创建出来且未调用start的线程
  • 可运行状态(Runnable)

    只有新建状态的线程调用start方法才能进入可运行状态
  • 受/锁阻塞状态(Blocked)

    当线程运行到需要抢到锁对象代码时,但是锁对象被其他线程持有,此时线程进入锁阻塞状态
  • 限时等待状态(Timed_waiting)

    当在线程中调用Thread.sleep(毫秒值)时,线程进入休眠状态
    注意:线程进入休眠状态,与是否持有锁无关, 是否持有锁对象都可以调用Thread.sleep方法 
    
    public class TestDemo {
        public static void main(String[] args) throws InterruptedException {
            Object obj = new Object();
            synchronized (obj){
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(500); //如果是抢到后调用sleep那么sleep期间不会释放锁(抱着锁睡!!)
                    System.out.println(i);
                }
            }
        }
    }    
  • 无限等待状态(Waiting)

    • 线程如何进入Waiting(无线等待状态)

      a.该线程必须先持有锁对象
      b.调用锁对象的wait()
      c.该线程会先自动释放锁对象,然后才能进入无限等待 
    • 其他线程如何唤醒Waiting状态的线程

      a.其他线程首先持有锁对象(该锁对象必须是进入无限等待的那个线程释放的锁对象)
      b.调用锁对象的notify()   
      c.被唤醒的线程,由于没有锁对象,先进入锁阻塞状态,只有再次持有刚刚的那个锁对象,才能进去可运行状态   
  • 消亡状态(Terminated)

    线程的任务执行完毕后,线程处于退出或者消亡状态

多线程的四种创建方式

继承Thread
实现Runnable接口
通过 Callable 和 Future 创建线程
利用线程池创建线程executorService

  1. 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。

  2. 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。

Java Thread 中 run() 与 start() 的区别

  • 1.start() 方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕,可以直接继续执行下面的代码;通过调用 Thread 类的 start() 方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此 Thread 类调用方法 run() 来完成其运行操作的, 这里方法 run() 称为线程体,它包含了要执行的这个线程的内容, run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。
  • 2.run() 方法当作普通方法的方式调用。程序还是要顺序执行,要等待 run 方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。

线程安全问题

产生原因:多个线程竞争同一资源(访问同一数据),可参考经典的生产者消费者问题。

解决方案:

run 方法内:同步代码块 synchronized {}

Public synchronized 返回值类型 方法名(){} 自动释放对象锁

使用 Lock 锁

Lock 锁需要程序员(在 finally 代码块中)手动释放。

Lock lock=new ReentranttLock()    // Reentrant(可重用的)

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作,是 JDK1.5 之后出现的。

Lock 接口中的方法:

void lock()   // 获取锁 
void unlock() // 释放锁 

Lock 接口的实现类:

java.util.concurrent.locks.ReentrantLock implements Lock

使用步骤:

  • 1.在成员位置创建一个 ReentrantLock 对象。
  • 2.在可能出现线程安全问题的代码前,调用 Lock 接口中的方法 lock 获取锁对象。
  • 3.在可能出现线程安全问题的代码后,调用 Lock 接口中的方法 unlock 释放锁对象。
public class RunnableImpl implements Runnable{
    //定义一个共享的票源
    private int ticket = 100;
    //1.在成员位置创建一个ReentrantLock对象
    Lock l = new ReentrantLock();
    //设置线程任务:卖票
    @Override
    public void run() {
        //使用死循环,让卖票重复的执行
        while(true){
            //2.在可能出现线程安全问题的代码前,调用Lock接口中的方法lock获取锁对象
            l.lock();
            //判断票是否大于0
            if(ticket>0){
                //为了提高线程安全问题出现的几率,让程序睡眠10毫秒
                try {
                    //可能会产生异常的代码
                    Thread.sleep(10);
                    //进行卖票 ticket--
                    System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票!");
                    ticket--;
                } catch (InterruptedException e) {
                    //异常的处理逻辑
                    e.printStackTrace();
                }finally {
                    //一定会执行的代码,一般用于资源释放(资源回收)
                    //3.在可能出现线程安全问题的代码后,调用Lock接口中的方法unlock释放锁对象
                    l.unlock();//无论程序是否有异常,都让锁对象释放掉,节约内存,提高程序的效率
                }
            }
        }
    }
}

集合和数组的区别

  • 数组是固定长度的;集合可变长度的。

  • 数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。

  • 数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。

常用的集合类

Map接口和Collection接口是所有集合框架的父接口
1.Collection接口的子接口包括:Set接口和List接口
2.Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
3.Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
4.List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等

图片说明

常见的数据结构

  • a.栈结构: 先进后出

  • b.队列结构: 先进先出

  • c.数组结构: 查询快,增删慢

  • d.链表结构: 查询慢,增删快

  • e.红黑树结构(了解)
    红黑树的特点: 查询速度非常恐怖
    树结构,二叉树,二叉查找树,平衡二叉树,....,黑红树

Collection

增: public boolean add(E e); 向集合中添加元素,返回值表示是否添加成功
删: public boolean remove(Object e); 从集合中删除某个元素,返回值表示是否删除成功
改: 无!
查: 无!
其他:
    public int size(); 获取元素个数
    public boolean isEmpty(); 判断集合是否为空集合(size==0?)
    public void clear(); 清空集合(把集合的所有元素都删除)
    public boolean contains(Object e); 判断集合中是否包含某元素 
    public Object[] toArray(); 将集合转成数组 
    public Iterator iterator(); 获取当前集合的迭代器对象   

List

a.有序的:在Java中元素存入和取出的顺序一致时,称为有序(比如:2 1 3 打印集合2 1 3)
b.有索引
c.元素是可重复   


List接口(继承Collection,肯定拥有Collection中的7+1)
    特有方法: 4个,增删改查一样一个,并且都和索引相关
        add(int index,E e); 在指定位置添加元素
        remove(int index); 删除指定位置的元素
        set(int index,E newE);将指定位置的元素改为新的元素
        get(int index);根据索引获取元素
    常见实现类:
        ArrayList<E> 底层就是采用数组结构,特点查询快,增删慢!
        LinkedList<E> 底层采用链表结构,特点就是查询慢,增删快!!   底层使用双向链表
        Vector<E>    

Set

a.无序(在Java中无序是指存入和打印时的顺序不一定一致,比如存入1 2 3 取出 1 3 2)
    b.无索引
    c.元素唯一    
    注意事项:
        做题的时候,列出Set接口的两个特点: b和c
        在Set接口中有一个实现类是有序的(LinkedHashSet有序的)  


Set接口的方法(继承Collection,那么就拥有7+1方法)
    特有方法: 无!!
    实现类:
        HashSet 没有特有方法 底层采用的是哈希表结构=数组结构+链表结构+红黑树结构(JDK1.8中加入的)
                特点:无序,无索引,元素唯一 
        LinkedHashSet 没有特有方法  底层采用的是链表+哈希表(链式哈希表)
                特点: 有序的,无索引,元素唯一
        TreeSet  没有特有方法 底层采用的是红黑树结构
                特点: 无序(但是它具有自然顺序)!!,无索引,元素唯一    
                注意: TreeSet是无序的,但是它是无序中一个比较特殊的存在
                    (存入132打印123,存入312打印123,存入321打印123,存入123打印123)  

常用集合

图片说明

Map

什么是Map集合:
    Map集合是一种双列集合!!
Map集合的特点:
    a.Collection的元素是单独存在的,Map的元素是成对存在的
    b.Collection称为单列集合,Map称为双列集合
    c.Map集合中键是唯一的,值是可以重复的,通过键最多只能找到与之对应的一个值 

a.Map<K,V>集合是双列集合的根接口,具有两个泛型
        其中K代表键的类型,V代表值的类,K的类和V的类型可以随意,可一样可不一样
b.Map接口有3个实现类:
    HashMap: 底层采用哈希表结构,无序的
    LinkedHashMap:底层采用链式哈希表,有序的
    TreeMap:底层采用红黑树结构, 无序的(会按照键的自然顺序默认升序)
    共同的特点: Map集合的所有实现类键都是唯一的,值是可以重复的
              如果键是自定义类型,为了保证键的唯一性,我们需要重写hashCode和equals方法  

Map集合的遍历方式

1.根据键找值
2.获取所有的键值对对象集合,通过迭代器 / 增强for遍历
3.通过对象中的所有values方法来找到值。

public class map集合遍历{
    public Static void main(String agrs){
        Set<String> keys = map.keySet();
            for(String key : keys ){
                 String values = key.getValues()
    } 

        Set<Map.Entry<String,String>> keyentry = map.keyEntry();
        Iterator<Map.Entry<String,String>> iterator = keyentry.iterator();
             while(iterator.hasNext()){
                   Map.Entry<String,String> entry = iterator.next();
                         String key = entry.getKey();
                         String value =entry.getValues;
                 }

            for(Map.Entry<String,String entry : iterator){
                       String key = entry.getKey();
                       String value =entry.getValues;
                }

            Collection<String> values = map.values;
                for(String value : values){
                         System.out.println(values);
                 }        
    }
}