56.整理JavaSE

封装继承多态的详细理解

封装

将数据和基于数据的操作封装在一起,数据被保护在抽象数据类型的内部,尽可能的隐藏内部的细节,只保留一些对外的接口使之与外部发生联系,其好处在于:

  • 可以对成员进行更加自由的修改,比如设置私有属性age的时候,通过函数来setAge(),这样就可以对入参进行判断
  • 隐藏实现细节,只对外提供必要的接口,使得程序的逻辑更好

继承

继承是使用已存在的类作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以使用父类的功能,但是不能选择性的继承父类,特点如下:

  • 子类可以拥有父类的所有属性和方法,但是父类中的私有属性和方法,子类是无法访问的,只能拥有,但是不能使用
  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
  • 子类可以用自己的方法重写父类的方法

构造器:

  • 构造器只能被调用,不能被继承,子类实例化对象的时候,如果不显示调用父类构造器,会默认调用父类的无参构造器,如果父类没有无参构造器,会导致编译无法通过
  • 如果子类需要使用有参构造器,必须显示的调用super(参数),且super必须是方法的第一行语句

继承的缺点:

继承是一种强耦合关系,父类边,子类就必须变,如果不需要使用向上转型,尽量使用组合与聚合代替继承

多态

多态指的是同一种行为具有不同的表现新式,运行同一段代码,运行时会根据调用对象的不同,而产生不同的结果,实现多态有两个必要条件:

  • 继承重写:子类继承父类并且重写父类方法
  • 向上转型:父类引用指向子类对象

多态的特点是:当父类对象引用变量引用子类对象的时候,被引用对象的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在父类中被定义过的,也就是被子类覆盖的方法;在Java中多态又分为继承多态和接口多态,以下是一个继承多态的实例,借这个实例理解一下该特点:

public class Main {
    public static void main(String[] args) {
        A a1=new A();
        A a2=new B();
        B b=new B();
        C c=new C();
        D d=new D();
        System.out.println("a1测试");
        a1.test(b);
        a1.test(c);
        a1.test(d);
        System.out.println("a2测试");
        a2.test(b);
        a2.test(c);
        a2.test(d);
        System.out.println("b测试");
        b.test(b);
        b.test(c);
        b.test(d);

    }
}
class A{
    public void test(D d){
        System.out.println("这是A中的参数D方法");
    }
    public void test(A a){
        System.out.println("这是A中的参数A方法");
    }
}
class B extends A{
    public void test(B b){
        System.out.println("这是B中的参数B方法");
    }
    public void test(A a){
        System.out.println("这是B中的参数A方法");
    }
}
class C extends B{

}
class D extends B{

}

分别对单个对象传入入参bcd,打印结果如下:

a1测试
这是A中的参数A方法
这是A中的参数A方法
这是A中的参数D方法
a2测试
这是B中的参数A方法
这是B中的参数A方法
这是A中的参数D方法
b测试
这是B中的参数B方法
这是B中的参数B方法
这是A中的参数D方法

a1的测试数据: 由于a1是A的对象,所以调用的方法限制在A中,传入bcd的对象,由于bc没有具体的方法,只能向上转型使用test(A a),而d可以使用test(D d)

a2的测试数据:在父类引用指向子类实例的时候,可以由如下的想法,先去父类中看有没有可以使用的方法,再去子类中看该方法有没有被重写,因此这种情况下B类的方法test(B b)实际上是被屏蔽的,这也是为什么a2.test(B b)没有被调用

b的测试数据:此时使用的是b实例,自然是先观察B类中可用的方法,对于参数b,自然调用test(B b),对于参数d,由于我们使用的是继承,继承了A中所有的方法,能找到具体的函数test(D d)那么就调用父类中的该方法,对于参数C,子类和父类都没有相似的方法,将C向上转型一次就可以使用B中的方法test(B b)

根据该例子我们可以总结出一套调用规则:

  • 对于可以向上转型的入参,总是会调用参数为最少向上转型次数的方法(包括0次),无论该方法在父类中还是子类中
  • 对于子类没有重写的方法,使用父类引用指向子类对象的时候,该方法是被屏蔽的
  • 对于重写的方法,使用父类引用指向子类对象的时候,调用父类中的该方法的时候,实际调用的是子类的该方法

Static关键字

当JVM加载一个类的时候,如果该类存在static修饰的成员变量或者方法,在类加载的时候就被被加载,同时被static修饰的成员变量和成员方法是被该类所有实例共享的,不依赖于某个特定的实例变量,其本质是将资源从实例相关变为类相关

  • 静态内部类:可以不依赖于外部类实例化,只能访问外部类的静态成员变量和静态方法,只加载一次
  • 静态方法:不能被重写,不能在内部使用this,super,非静态方法和遍历,在类加载的时候就会被分配和加入内存,可以使用类名.方法名的方法调用
  • 静态变量:也叫全局变量,内存中仅有一个,其他性质参考静态方法
  • 静态代码块:与静态方法差不多
  • 静态导包:用于导入某个类中的指定静态资源,使用后不需要使用类名调用,而是直接可以使用静态成员变量和方法
  • 非静态内部类不可以定义静态成员和方法,但是可以访问外部类的所有内容,通过new外部类构造器.new 内部类构造器来创建对象
  • this指针其实是可以访问static的变量的,但即使是访问静态变量也不能在静态代码块或者静态函数中使用this指针
  • 外部类可以访问静态内部类的所有资源,在外部类的外部也可以直接实例化静态内部类
  • 静态内部类可以拥有静态和非静态成员,但是只能访问外部类的静态成员

反射?

在程序运行时,动态加载类并获取类的详细信息,从而操作类或对象的属性和方法,本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息

优点:在运行时获得类的各种内容,进行反编译,能让我们方便的灵活的创建代码,无需在组件之间进行源代码的链接

缺点:反射会消耗一定的系统资源,并且会破坏封装性而导致安全问题

用途:

  • 反编译:.class->.java
  • 能让我们访问对象的任意属性方法
  • 手动加载某个类:比如加载数据库驱动class.forname("com.sql.jdbc.driver")
  • 最终要的用途是开发各种通用的框架,比如spring,需要根据配置文件加载不同的类或者是对象,调用不同的方法,这是就必须用到反射在运行时动态加载需要加载的对象

获取class对象的方法

类名.class

对象.getClass()

Class.forName("类的全限定名")

float n=3.4有没有问题?

3.4是双精度数,将double赋值给float属于向下转型,可能会造成精度损失,所以必须进行强制类型转换,应该写成3.4f

泛型和泛型擦除是什么?

泛型的本质是参数化类型,泛型提供了编译时类型的安全检测机制,该机制允许程序在编译时检测非法类型

在编译阶段采用泛型时加上的类型参数,最终会被编译器去掉,这个过程称为类型擦除,因此泛型主要用于编译阶段,在编译后生成的Java字节码文件中不包含泛型中的类型信息

泛型标记的规范:

  • E:在集合中使用,表示存放在集合中的元素
  • T:表示Java类,包括基本的类以及自定义的类
  • K,V:kv键值对中的k,v
  • N:表示数值类型
  • ?:表示不确定的Java类型

Queue接口中add/offer,remove/poll,element/peek方法的区别

add/offer:都是向队列尾部增加一个元素,区别是当超出队列界限时,add会抛出异常,offer会返回false

remove/poll:当队列为空的时候,remove会抛出异常,poll会返回null

element/peek:当队列为空时,element会抛出异常,peek会返回null

volatile关键字的作用?

保证可见性,在线程修改变量的值之后,新的值对于其他线程是可以立即获取的

禁止指令重排,被修饰的变量不会被缓存在寄存器中或者对其他处理器不可见的地方,因此在读取volatile修饰的变量时总会返回最新写入的值

不会执行加锁操作,不会导致线程阻塞

volatile可以保证对单词读写操作的原子性,但是不能保证像i++这种操作的原子性,本质上i++这个操作时读写两次操作

volatile底层使用内存屏障来实现,加入volatile关键字的时候,会多出一个lock前缀指令,该lock就是内存屏障,提供以下三个功能:

  1. 确保指令重排时不会把后面的指令排到内存屏障之前的位置,也不会吧前面的指令排到内存屏障之后的位置,即在执行到内存屏障的时候,在他前面的操作已经全部完成,
  2. 它会强制对缓存的修改操作立即写入主存,
  3. 如果是写操作,会导致其他cpu中对应的缓存行无效

Synchronized的内部包括哪些区域?

ContentionList:锁竞争队列,所有请求锁的线程都被放在竞争队列中

EntryList:竞争候选列表,在锁竞争队列中有资格称为候选者来竞争锁资源的线程被移动到候选列表

WaitSet:等待集合,调用wait方法后阻塞的线程被放在WaitSet

OnDeck:竞争候选者,在同一时刻最多只有一个线程在竞争锁资源,此线程的状态称为OnDeck

Owner:竞争到锁资源的线程状态

!Owner:释放锁后线程的状态

简述Synchronized的实现原理

  • 收到新的锁请求的时候先进行自旋,如果自旋也没获取到锁的资源,被放入ContentionList
  • 为了防止ContentionList尾部的元素被大量线程进行CAS访问影响性能,Owner线程会在释放锁时将ContentionList的部分线程移动到EntryList并且指定某个线程为OnDeck线程,Owner线程并没有将锁直接给他而是把锁竞争的权利交给了它,该行为叫竞争切换,牺牲了公平性但是提高了性能
  • 获取到锁的OnDeck线程会变为Owner线程,未获取到的仍停留在EntryList中
  • Owner线程在被wait阻塞后会进入WaitSet,直到某个时刻被唤醒再次进入EntryList
  • ContentionList,EnrtyList,WaitSet中的线程都处于阻塞状态
  • 当Owner线程执行完毕后会释放锁资源并且变为!Owner状态

jdk1.6中引入了适应自选,锁消除,锁粗化,轻量级锁以及偏向锁等提高锁的效率,锁可以以偏向锁->轻量级锁->重量级锁方式升级,这种过程也称为锁膨胀

Synchronized可以修饰静态方法,但是不能修饰静态代码块,当修饰静态方法的时候,监视器锁就是对象的class实例,因为class位于方法区,因此对于该类来说,此锁是全局的

ReentrantLock?

是Lock接口的实现类,是一个可重入的独占锁,通过AQS实现:

  • 支持公平,非公平锁
  • 提供可响应中断锁(通过interrupt方法中断取消对锁的请求)
  • 可轮询锁:通过tryLock获取锁,有可用的锁就立刻返回true否则返回false
  • 定时锁:带有long参数的tryLock方法,在给定时间获取到锁且当前线程未被中断返回true,超过时间返回false,如果获取锁时被中断则抛出异常并且清除已终止状态
  • 通过lock和unlock显示的加锁释放锁
  • 相比于synchronized它采用的是乐观并发而不是悲观并发,它会先尝试使用cas的方式获取锁

Lock接口的方法?

lock/unlock:加锁和解锁

newCondition:创建条件对象,使用条件对象管理那些已经获取了锁但是不满足有效条件的线程,调用await方法把线程进入等待集,调用sign/signall解除阻塞

lockInterruptibly:如果当前线程未被中断则获取该锁

自旋锁?

自旋锁任务如果持有锁的线程能在很短的时间内释放锁的资源,那么等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,只需等待小段时间,避免了切换线程的时间消耗

  • 其优点在于减少了cpu上下文切换,对于占用锁时间非常短或锁竞争不激烈的代码块来说性能好
  • 缺点在于在持有锁线程长时间占有锁或竞争过于激烈的时候,线程会长时间自旋浪费cpu资源

锁的状态?

锁升级过程:无锁->(访问同步块的时候升级为偏向锁)->偏向锁->(有锁竞争的时候升级为轻量级锁)->(自旋十次失败升级为重量级锁)重量级锁

  • 重量级锁:基于操作系统互斥量实现,会导致进程在用户态和内核态之间的切换,开销大
  • 轻量级锁:在没有多线程竞争的前提下,减少重量级锁的使用,适用于线程交替执行同步代码块的情况,如果同一时刻多线程访问同一个锁,会导致轻量级锁膨胀为重量级锁
  • 偏向锁:用于某个线程获取锁之后,消除这个线程锁重入的开销,第一次到达同步代码块的时候,锁对象变为偏向锁,执行完同步代码块后,线程不会主动释放锁,当此线程再次到达同步代码块的时候,会查看该锁的偏向是不是自己,如果是就不需要重新加锁,多次cas操作被简化成了一次cas操作

如何进行锁的优化?

  • 减少锁的持有时间:减少同步代码块对锁的持有时间
  • 减小锁的粒度:segment细化
  • 读分离:读写锁
  • 锁消除
  • 锁粗化:锁分的太细反而影响性能,此时可以将关联性强的锁集中处理

注解是什么?

是一种标记,可以是类或者接口附加额外的信息,是帮助编译器和JVM完成一些特定的功能的

元注解是自定义注解使用的注解包括@Target:约束注解的位置,@Rentention:用来约束注解的生命周期@Document:表面这个注解应该被javadoc工具记录@Inherited:表明某个注解类型是被继承的

File对象是什么,常用方法?

File对象表示的是操作系统上的文件或者目录

  • getAbsolutePath():获取绝对路径
  • getPath:获取文件定义时使用的路径
  • getName:获取文件名
  • lengh:返回文件长度,单位是字节
  • exists:判断file对象表示的文件或目录是否存在
  • isDirctory:判断是否为目录
  • isFile:判断是否为文件
  • createNewFile:不存在的时候创建新文件
  • delete:删除文件
  • mkdir:创建一级目录
  • mkdirs:创建多级目录
  • list:获取当前目录下所有一级文件名,到一个字符串数组返回
  • listFiles:获取当前目录下所有一级File对象到File数组返回

异常处理的方式?

抛出异常:遇到异常不进行具体的处理,而是将异常抛给调用者,throws作用在方法上,throw作用在方法内

try/catch/finally捕获异常:jdk1.7开始可以在try后的小括号定义需要关闭的资源,如果资源实现了autoCloseable接口就可以实现自动关闭

如果想在循环里删除元素该怎么做?

使用迭代器,调用集合类的iterator.remove,可以在循环中对元素进行删除

使用集合类的removeIf方法,传入一个bool表达式

使用fori循环进行删除的时候需要重置i的位置,即--i;

不能使用foreach遍历的时候进行修改或者删除操作

多线程不可见问题的原因和解决方式

不可见的原因是,每个线程都有自己的工作内存,线程都是从主内存拷贝共享变量的副本值,每个线程都是在自己工作内存操作共享变量的

加锁:获得锁之后的线程会将工作内存清空,从主内存拷贝共享变量最新的值称为副本,修改后刷新回主内存,再释放锁

volatile:该关键字修饰的变量会通知其他线程之前读取的值已经失效,线程会加载最新的值到自己的工作内存

Java为什么可以一次编译,到处运行?

java需要经过编译器编译成字节码,在程序运行时,jvm将字节码翻译成特定平台的机器码并运行,只要在不同平台安装对应的jvm,就可以运行字节码文件,同一份Java源代码在不同的平台上运行,不需要做任何改变,只需要编译一次

为什么构造方法不能被重写?

构造方法需要和类保持同名,重写要求的是子类方法要和父类方法保持同名,允许构造方法重写的话,子类中将会存在与类名不同的构造方法

对线程重复启动会发生什么?

触发IlligalThreadStateException

如果不使用synchronized和lock有没有办法做到线程安全?

volatile,不可变对象,ThreadLocal,原子类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用IDEA 2023.2.1创建一个普通JavaSE项目,您可以按照以下步骤进行操作: 1. 首先,确保您已经安装了Java开发工具包(JDK)。您可以在引用中提供的路径中找到JDK的安装目录。 2. 打开IDEA,并选择"Create New Project"(创建新项目)选项。 3. 在弹出的对话框中,选择"Java",然后选择"JavaSE"作为项目类型。 4. 在项目设置中,选择您想要的项目名称和存储位置。 5. 确保在"Project SDK"(项目SDK)下拉菜单中选择正确的JDK版本。如果没有找到您的JDK版本,请确保您已正确安装了JDK,并在IDEA的设置中配置了JDK的路径。 6. 单击"Next"(下一步)继续进行。 7. 在"Additional Libraries and Frameworks"(附加库和框架)部分,您可以选择添加其他库或框架,或者直接单击"Finish"(完成)创建项目。 至此,您已成功使用IDEA 2023.2.1创建了一个普通的JavaSE项目。请注意,这是一种常见的创建JavaSE项目的方法,具体步骤可能会因IDEA版本而有所不同。如果您需要更详细的指导,请参考IDEA的官方文档或在线教程。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Mac/Win最新IntelliJ IDEA2023.1详细安装与配置使用教程(亲测有效,持续更新)](https://blog.csdn.net/Sunshine_Mr_Sun/article/details/123891067)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [如何在IDEA中新建一个WEB项目(2020.3、2021.1)](https://blog.csdn.net/xcj2317496650/article/details/115936438)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值