java基础相关

Posted 青春无敌美少

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java基础相关相关的知识,希望对你有一定的参考价值。

1.接口和抽象类

1).接口的特点

不能实例化
没有构造方法
方法默认public abstract修饰
变量默认public static final修饰

2).抽象类特点

继承了抽象类的子类,要么对父类的抽象方法进行重写,要么自己也是抽象类
抽象类也可以拥有普通方法
抽象类不能创建对象
抽象类也有构造方法,但是是为了子类创建对象使用

3).接口和抽象类相同点

都是不断抽取出来的抽象概念

4).接口和抽象类的区别

接口是行为的抽象,是一种行为的规范,接口是like a 的关系;抽象是对类的抽象,是一种模板设计,抽象类是is a 的关系。
接口没有构造方法,而抽象类有构造方法,其方法一般给子类使用
接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
抽象体现出了继承关系,继承只能单继承。接口提现出来了实现的关系,实现可以多实现。接口强调特定功能的实现,而抽象类强调所属关系。
接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。

2.抽象类和普通类的区别

抽象类不能被实例化,抽象类的子类必须实现所有的抽象方法才能被实例化;普通类可以实例化。

抽象类必须用关键字abstract修饰;普通类不使用关键字abstract。

抽象类中的抽象方法只被声明,没有方法体;普通类有方法体。

抽象方法不能是private的,因为抽象类必须被子类继承进行方法重写;普通方法可以是public、private、protected的。

抽象类中可以没有抽象方法,但抽象方法必须在抽象类中;普通类没有抽象方法。

子类继承抽象类之后,必须重写抽象类所有的抽象方法,否则子类必须设置为抽象类。

抽象类可以使用多态;普通类不可以。

3.“==”和equals的区别

“==”和equals 最大的区别是
“==”是运算符,如果是基本数据类型,则比较存储的值;如果是引用数据类型,则比较所指向对象的地址值。
equals是Object的方法,比较的是所指向的对象的地址值,一般情况下,重写之后比较的是对象的值。
一、“==”
 “==”是运算符
如果比较的对象是基本数据类型,则比较的是其存储的值是否相等;
如果比较的是引用数据类型,则比较的是所指向对象的地址值是否相等(是否是同一个对象)。

二、equals()
equals是Object的方法,用来比较两个对象的内容是否相等。

注意:
equals 方法不能用于比较基本数据类型,如果没有对 equals 方法进行重写,则相当于“==”,比较的是引用类型的变量所指向的对象的地址值。

一般情况下,类会重写equals方法用来比较两个对象的内容是否相等。比如String类中的equals()是被重写了,比较的是对象的值。

4.Set和Map区别

  1. Map是键值对,Set是值的集合,当然键和值可以是任何的值;

  2. Map可以通过get方法获取值,而set不能因为它只有值;

  3. 都能通过迭代器进行for…of遍历;

  4. Set的值是唯一的可以做数组去重,Map由于没有格式限制,可以做数据存储

  5. map和set都是stl中的关联容器,map以键值对的形式存储,key=value组成pair,是一组映射关系。set只有值,可以认为只有一个数据,并且set中元素不可以重复且自动排序。

5.互斥锁与同步锁的区别

1.1 互斥
所谓互斥,就是不同线程,通过竞争进入临界区(共享的数据和硬件资源),为了防止访问冲突,在有限的时间内只允许其中之一独占性的使用共享资源。如不允许同时写。

1.2 同步
同步关系则是多个线程彼此合作,通过一定的逻辑关系来共同完成一个任务。一般来说,同步关系中往往包含互斥,同时,对临界区的资源会按照某种逻辑顺序进行访问。如先生产后使用。

互斥是通过竞争对资源的独占使用,彼此之间不需要知道对方的存在,执行顺序是一个乱序。
同步是协调多个相互关联线程合作完成任务,彼此之间知道对方存在,执行顺序往往是有序的。

互斥就是线程 A 访问了一组数据,线程 BCD 就不能同时访问这些数据,直到 A 停止访问了
同步就是 ABCD 这些线程要约定一个执行的协调顺序。比如 D 要执行,B 和 C 必须都得做完,而 B 和 C 要开始,A 必须先得做完

6.读写锁

7.session和cookie的区别

1.保存的位置不同
cookie保存在浏览器端,session保存在服务端。
2.使用方式不同
cookie如果在浏览器端对cookie进行设置对应的时间,则cookie保存在本地硬盘中,此时如果没有过期,则就可以使用,如果过期则就删除。如果没有对cookie设置时间,则默认关闭浏览器,则cookie就会删除。
session:我们在请求中,如果发送的请求中存在sessionId,则就会找到对应的session对象,如果不存在sessionId,则在服务器端就会创建一个session对象,并且将sessionId返回给浏览器,可以将其放到cookie中,进行传输,如果浏览器不支持cookie,则应该将其通过encodeURL(sessionID)进行调用,然后放到url中。
3.存储内容不同:cookie只能存储字符串,而session存储结构类似于hashtable的结构,可以存放任何类型。
4.存储大小:cookie最多可以存放4k大小的内容,session则没有限制。
5.session的安全性要高于cooKie
6.cookie的session的应用场景:cookie可以用来保存用户的登陆信息,如果删除cookie则下一次用户仍需要重新登录
session就类似于我们拿到钥匙去开锁,拿到的就是我们个人的信息,一般我们可以在session中存放个人的信息或者购物车的信息。
7.session和cookie的弊端:cookie的大小受限制,cookie不安全,如果用户禁用cookie则无法使用cookie。如果过多的依赖session,当很多用户同时登陆的时候,此时服务器压力过大。sessionId存放在cookie中,此时如果对于一些浏览器不支持cookie,此时还需要改写代码,将sessionID放到url中,也是不安全。

8.重写和重载

重写:在子类中把父类本身有的方法重新写一遍。子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的情况下, 对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。
重载:在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不同)则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,但不能通过返回类型是否相同来判断重载。

区别:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。

9.内存溢出和内存泄漏的区别及详解

jvm内存除了程序计数器不会发生内存溢出,其余的都可能存在内存溢出。

  1. 内存溢出(Out Of Memory)

是程序在申请内存时,没有足够的内存空间供其使用。比如:你需要10M的空间,内存空间只剩8M,这就会出现内存溢出。
以栈举例:栈满时在做进栈必定产生空间溢出,叫上溢,栈空时在做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出。

  1. 内存泄漏 (Memory Leak)

是程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重。memory leak最终会导致out of memory。
这块内存不释放,就不能再用了,就叫这块内存泄漏了。

内存泄漏分类

以发生的方式来分类,内存泄漏可以分为4类:

  1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
  2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
  3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
  4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。

从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。
真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较常发性和偶发性内存泄漏它更难被检测到。

3.内存溢出的原因及解决方案:

修改JVM启动参数,直接增加内存。
检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
对代码进行走查和分析,找出可能发生内存溢出的位置。
使用内存查看工具动态查看内存使用情况。

4.出现内存溢出和内存泄露,重点排查几点:

1、检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条以上记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询,查多少字段用多少字段。
2、检查代码中是否有死循环或递归调用。
3、检查是否有大循环重复产生新对象实体。
4、检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。使用list.clear()或者map.clear()

10.TCP和UDP的区别及应用场景

一、TCP和UDP的区别

  1. 连接
    TCP: 是面向连接的传输协议,即:传输数据之前必须先建立好连接。
    UDP: 是属于无连接的传输协议。
  2. 服务对象
    TCP: 是点对点的两点之间的服务,即:一条TCP连接只能有两个端点。
    UDP: 支持一对一、一对多、多对一、多对多的交互通信。
  3. 可靠性
    TCP: 是可靠的传输协议,进行的是可靠交付,即:无差错、不丢失、不重复、按序到达等。
    UDP: 是不可靠的传输协议,只是尽最大努力交付,不能保证可靠交付。
  4. 拥塞控制&流量控制
    TCP: 有拥塞控制和流量控制保证数据传输的安全性。
    UDP: 没有拥塞控制,当网络拥塞时不会影响源主机的发送效率。
  5. 报文长度
    TCP: 属于“动态报文长度”,即:TCP的报文长度是根据接收方的窗口大小和当前网络拥塞情况来决定的。
    UDP: 是面向报文的,它是保留上面传下来的报文边界,不合并,不拆分。
  6. 首部开销
    TCP: 首部开销大,因为首部就占了20个字节。
    UDP: 首部开销相对小一点,只占了8个字节。 (eg: UDP的8个字节分布在:源端口、目的端口、数据长度、校验和)
    二、TCP和UDP的适用场景
    根据上述特点,可知:
    TCP 是可靠的传输协议,但传输速度慢;
    而UDP 是不可靠的传输协议,但传输速度快;
    TCP的适用场景: 要求通信数据的完整性和准确性较高,eg: 文件传输、邮件发送与接收、重要状态的更新等。
    UDP的适用场景: 要求通信的响应度高,对数据信息的安全性和完整性要求相对较低,常用于实时通信,eg: 网络电话、网络视频、实时通信等。

11.堆与栈的区别

1、栈由系统自动分配,而堆是人为申请开辟;
2、栈获得的空间较小,而堆获得的空间较大;
3、栈由系统自动分配,速度较快,而堆一般速度比较慢;
4、栈是连续的空间,而堆是不连续的空间。

12.栈和队列的区别

一、规则不同
1、 队列:先进先出(First In First Out)FIFO
2. 栈:先进后出(First In Last Out )FILO
二、对插入和删除操作的限定不同
1、 队列:只能在表的一端进行插入,并在表的另一端进行删除;
3. 栈:只能在表的一端插入和删除。
三、遍历数据速度不同
1、 队列:基于地址指针进行遍历,而且可以从头部或者尾部进行遍历,但不能同时遍历,无需开辟空间,因为在遍历的过程中不影响数据结构,所以遍历速度要快;
4. 栈:只能从顶部取数据,也就是说最先进入栈底的,需要遍历整个栈才能取出来,而且在遍历数据的同时需要为数据开辟临时空间,保持数据在遍历前的一致性。

13.Java 内存区域

  1. 程序计数器(Program Counter Register)
      程序计数器是一块较小的内存空间,他可以看作是当前线程所执行字节码的行号指示器。在Java虚拟机的概念模型中,字节码解释器的工作就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

  2. 虚拟机栈(VM Stack)
      虚拟机栈的生命周期与线程相同,虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每个方法被调用直至执行完毕的过程,就对应一个栈帧在虚拟机中从入栈到出栈的过程。

    局部变量表中存放了编译期可知的各种Java虚拟机基本数据类型(byte、short、char、int、long、float、double、boolean)、对象引用(reference类型,它并不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他榆次对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。

  3. 本地方法栈(Native Method Stacks)
      本地方法栈与虚拟机栈的作用基本上是一致的,区别在于虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。对应本地方法栈的实现,《Java虚拟机规范》中并未强制规定,由虚拟机自己根据需求实现,有的Java虚拟机例如Hot-Spot虚拟机就直接将本地方法栈和虚拟机栈合二为一。与虚拟机栈相同的是,本地方法栈也会出现两类异常状况。

  4. 堆(Heap)
      对于Java应用程序来说,Java堆(Heap)是虚拟机所管理的内存中最大的一块区域,Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存存在的唯一目的就是存放对象实例,几乎所有的对象实例都在堆上分配空间、存放。

  5. 方法区(Method Area)
      方法区与Java堆一样,是各个线程共享的内存区域,它用于存放已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

  6. 运行时常量池(Runtime Constant Pool)
      运行时常量池(Runtime Constant Pool)是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Tabel),用于存放编译器内生成的各种字面量与符号引用,这部分内容将在内加载后存放到方法区的运行时常量池中。

14.访问控制修饰符


public:都可以访问;

protected:本类、本包以及所有子类中可以访问;

defalut:本类、本包中访问;

private:只能在本类中访问。

15.线程中的start()方法和run()的区别

1、线程中的start()方法和run()方法的主要区别在于,当程序调用start()方法,将会创建一个新线程去执行run()方法中的代码。但是如果直接调用run()方法的话,会直接在当前线程中执行run()中的代码,注意,这里不会创建新线程。这样run()就像一个普通方法一样。

2、另外当一个线程启动之后,不能重复调用start(),否则会报IllegalStateException异常。但是可以重复调用run()方法。

总结起来就是run()就是一个普通的方法,而start()会创建一个新线程去执行run()的代码。

16.为什么使用线程池?

线程池是运用场景最多的并发框架,几乎所有需要一步或者并发执行任务的程序都可以使用线程池。使用线程池一般有以下三个好处:

①降低资源的消耗,通过重复利用已经创建的线程降低线程创建和销毁造成的消耗。

②提高相应速度,当任务到达的时候,任务可以不需要等到线程创建就能立刻执行。

③提高线程的可管理性,线程是稀缺资源,使用线程池可以统一的分配、调优和监控。

以上是关于java基础相关的主要内容,如果未能解决你的问题,请参考以下文章

案例-- 线程不安全对象(SimpleDateFormat)

2021 年 五一数学建模比赛 C 题

2021 年 五一数学建模比赛 C 题

关于GC(中):Java垃圾回收相关基础知识

内存溢出(Oom)和内存泄露(Memory leak)

java内存泄漏与处理