animal.eat编译看左边,编译的时候会先看Animal里面有什么方法可以调用,bark没有在Animal中所以调用会直接报错。
运行看右边,animal.speak()运行的时候看右边类型,Animal和Dog都有这个方法,但是new的时候是Dog类型的,所以结果应该是汪汪汪
对象引用转换问题
向上转型是自动进行的,而且是安全的,如下:
class Animal {} class Dog extends Animal {} Dog dog = new Dog(); Animal animal= dog;//自动向上转型但是向下转型需要手动进行,并且存在风险。如果父类对象实际上并不是目标子类的实例,在转型时就会抛出异常:
Animal animal = new Animal();//需要改成Animal animal = new dog(); Dog dog =(Dog)animal;// 运行时抛出classCastException解決方式是需要使用instanceof检查:
if (animal instanceof Dog) { Dogdog = (Dog)animal;//只有确认animal是Dog的实例时才进行转型为什么用bigDecimal 不用double?
使用BigDecimal可以确保精确的十进制数值计算,避免了使用double可能出现的舍入误差。需要注意的是,在创建BigDecimal对象时,应该使用字符串作为参数,而不是直接使用浮点数值,以避免浮点数精度丢失。
怎么把一个对象从一个jvm转移到另一个jvm?
使用序列化和反序列化:将对象序列化为字节流,并将其发送到另一个VM,然后在另一个VM中反序列化字节流恢复对象。这可以通过 Java 的 ObjectOutputStream 和 ObjectlnputStream 来实现。
使用消息传递机制:利用消息传递机制,比如使用消息队列(如RabbitMQ、Kafka)或者通过网络套接字进行通信,将对象从一个VM发送到另一个。这需要自定义协议来序列化对象并在另一个VM中反序列化。
使用远程方法调用(RPC):可以使用远程方法调用框架,如gRPC,来实现对象在不同JVM之间的传输。远程方法调用可以让你在分布式系统中调用远程MM上的对象的方法。
使用共享数据库或缓存:将对象存储在共享数据库(如MySQL、PostgreSQL)或共享缓存(如Redis)中,让不同的MM可以访问这些共享数据。这种方法适用于需要共享数据但不需要直接传输对象的场景。
序列化和反序列化让你自己实现你会怎么做?
Java 默认的序列化虽然实现方便,但却存在安全漏洞、不跨语言以及性能差等缺陷。
无法跨语言:Java序列化目前只适用基于Java语言实现的框架,其它语言大部分都没有使用Java的序列化框架,也没有实现Java序列化这套协议。因此,如果是两个基于不同语言编写的应用程序相互通信,则无法实现两个应用服务之间传输对象的序列化与反序列化。
容易被攻击:Java序列化是不安全的,我们知道对象是通过在ObjectlnputStream上调用readObject0方法进行反序列化的,这个方法其实是一个神奇的构造器,它可以将类路径上几乎所有实现了Serializable 接口的对象都实例化。这也就意味着,在反序列化字节流的过程中,该方法可以执行任意类型的代码,这是非常危险的。
序列化后的流太大:序列化后的二进制流大小能体现序列化的性能。序列化后的二进制数组越大,占用的存储空间就越多,存储硬件的成本就越高。如果我们是进行网络传输,则占用的带宽就更多,这时就会影响到系统的吞吐量。
我会考虑用主流序列化框架,比如FastJson、Protobuf来替代Java 序列化。
如果追求性能的话,Protobuf序列化框架会比较合适,Protobuf 的这种数据存储格式,不仅压缩存储数据的效果好,在编码和解码的性能方面也很高效。Protobuf 的编码和解码过程结合.proto文件格式,加上 Protocol Buffer独特的编码格式,只需要简单的数据运算以及位移等操作就可以完成编码与解码。可以说 Protobuf 的整体性能非常优秀。
有一个学生类,想按照分数排序再按照学号排序,应该怎么做
学生类实现Comparable接口,重写compareTo方法,,使用时直接用Collections.sort(Student);即可
public class Student implements Comparable<Student> { private int id; private int score; //构造方法和其他属性、方法省略 @override public int compareTo(Student other) { if (this.score != other.score) { return Integer.compare(other.score, this.score); } else { return Integer.compare(this.id,other.id);// 如果 } } } List<Student> students = new ArrayList<>();//添加学生对象到列表中 Collections.sort(students);volatile和sychronized如何实现单例模式
public class SingleTon { // volatile 关键字修饰变量 防止指令重排序 private static volatile SingleTon instance = null; private SingleTon(){} public static SingleTon getInstance(){ if(instance == null){ //同步代码块只有在第一次获取对象的时候会执行到,第二次及以后访问时instance变量均非null故不会往下执行了 直接返回啦 synchronized(SingleTon.class){ if(instance == null){ instance = new SingleTon(); } } } return instance; } }代理模式和适配器模式有什么区别?
目的不同:代理模式主要关注控制对对象的访问,而适配器模式则用于接口转换,使不兼容的类能够一起工作。
结构不同:代理模式一般包含抽象主题、真实主题和代理三个角色,适配器模式包含目标接口、适配器和被适配者三个角色。
应用场景不同:代理模式常用于添加额外功能或控制对对象的访问,适配器模式常用于让不兼容的接口协同工作。
介绍一下策略模式和责任链模式,分别用在哪些场景?
策略模式定义一系列算法,把它们一个个封装起来,并且使它们可相互替换。从而让算法可独立于使用它的客户而变化。
假设你正在开发一个电商网站的支付模块。用户可以选择支付宝、微信支付或者银行卡支付。每种支付方式的底层逻辑(比如调用的API、加密方式、参数构造)都完全不同,但对于调用支付功能的上层业务逻辑来说,它只需要调用一个统一的“支付”方法即可,无需关心具体是哪种支付。
这就是策略模式的用武之地。我们把每种支付方式(支付宝、微信)都封装成一个独立的“策略”类,它们都遵循一个共同的接口。上层业务代码通过这个共同的接口来调用支付功能,并可以在运行时根据用户的选择,动态地切换不同的策略。
所以,策略模式适用场景如下:
- 当一个系统需要动态地在几种算法中选择一种时。
- 当一个对象有很多行为,不想使用大量的if-else或switch-case语句来选择时。
- 当你希望算法的变化不会影响到使用它的客户端时。
责任链模式核心思想是为请求创建一个接收者对象的链。对请求的发送者和接收者进行解耦,让多个对象都有机会处理这个请求。请求沿着链传递,直到链上的一个对象处理它为止。
想象一下公司里的请假审批流程。一个员工要请假,他首先会把请假条交给自己的直属经理。如果请假天数很短(比如1天),经理自己就能批准。如果天数较长(比如5天),经理没有权限,就需要把请假条上报给部门总监。总监也有自己的审批权限上限,如果超过了,就需要继续上报给总经理。
在这个过程中,请假条(请求)就在一条由经理->总监->总经理(接收者链)上传递,直到某个角色(接收者)能够处理它。发送请假条的员工(客户端)并不知道最终是谁批准了他的假,他只需要把请求提交给链的第一个节点(经理)即可。
所以,责任链模式适用场景总结:
- 当有多个对象可以处理同一个请求,但具体由哪个对象处理需要在运行时决定时。
- 当你希望请求的发送者和接收者解耦,不直接通信时。
- 当你需要动态地指定和修改处理一个请求的对象集合时。
Native方法解释一下
在Java中,native方法是一种特殊类型的方法,它允许Java代码调用外部的本地代码,即用C、C++或其他语言编写的代码。native关键字是Java语言中的一种声明,用于标记一个方法的实现将在外部定义。
在Java类中,native方法看起来与其他方法相似,只是其方法体由native关键字代替,没有实际的实现代码。例如:
public class NativeExample { public native void nativeMethod(); }要实现native方法,你需要完成以下步骤:
1.生成JNI头文件:使用javah工具从你的Java类生成C/C++的头文件,这个头文件包含了所有native方法的原型。
2.编写本地代码:使用C/C++编写本地方法的实现,并确保方法签名与生成的头文件中的原型匹配。
3.编译本地代码:将C/C++代码编译成动态链接库(DLL,在Windows上),共享库(SO,在Linux上)
4.加载本地库:在Java程序中,使用System.loadLibraryO方法来加载你编译好的本地库,这样VM就能找到并调用native方法的实现了。