java 复制对象_Java程序员必备:序列化全方位解析

前言
相信大家日常开发中,经常看到Java对象“implements Serializable”。那么,它到底有什么用呢?本文从以下几个角度来解析序列这一块知识点~

  • 什么是Java序列化?
  • 为什么需要序列化?
  • 序列化用途
  • Java序列化常用API
  • 序列化的使用
  • 序列化底层
  • 日常开发序列化的注意点
  • 序列化常见面试题

一、什么是Java序列化?

  • 序列化:把Java对象转换为字节序列的过程
  • 反序列:把字节序列恢复为Java对象的过程

91f3922c673f37d5d4a59ef91b26cbf5.png

二、为什么需要序列化?
Java对象是运行在JVM的堆内存中的,如果JVM停止后,它的生命也就戛然而止。

a476bf9b029676df7ee2069d0c43790f.png

如果想在JVM停止后,把这些对象保存到磁盘或者通过网络传输到另一远程机器,怎么办呢?磁盘这些硬件可不认识Java对象,它们只认识二进制这些机器语言,所以我们就要把这些对象转化为字节数组,这个过程就是序列化啦~
打个比喻,作为大城市漂泊的码农,搬家是常态。当我们搬书桌时,桌子太大了就通不过比较小的门,因此我们需要把它拆开再搬过去,这个拆桌子的过程就是序列化。 而我们把书桌复原回来(安装)的过程就是反序列化啦。
三、序列化用途
序列化使得对象可以脱离程序运行而独立存在,它主要有两种用途:

86a59aa5baa4ddc5823828f6482dfc62.png
  • 1) 序列化机制可以让对象地保存到硬盘上,减轻内存压力的同时,也起了持久化的作用;

比如 Web服务器中的Session对象,当有 10+万用户并发访问的,就有可能出现10万个Session对象,内存可能消化不良,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

  • 2) 序列化机制让Java对象在网络传输不再是天方夜谭。

我们在使用Dubbo远程调用服务框架时,需要把传输的Java对象实现Serializable接口,即让Java对象序列化,因为这样才能让对象在网络上传输。
四、Java序列化常用API
java.io.ObjectOutputStream java.io.ObjectInputStream java.io.Serializable java.io.Externalizable 复制代码
Serializable 接口
Serializable接口是一个标记接口,没有方法或字段。一旦实现了此接口,就标志该类的对象就是可序列化的。
public interface Serializable { } 复制代码
Externalizable 接口
Externalizable继承了Serializable接口,还定义了两个抽象方法:writeExternal()和readExternal(),如果开发人员使用Externalizable来实现序列化和反序列化,需要重写writeExternal()和readExternal()方法
public interface Externalizable extends java.io.Serializable { void writeExternal(ObjectOutput out) throws IOException; void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; } 复制代码
java.io.ObjectOutputStream类
表示对象输出流,它的writeObject(Object obj)方法可以对指定obj对象参数进行序列化,再把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream
表示对象输入流, 它的readObject()方法,从输入流中读取到字节序列,反序列化成为一个对象,最后将其返回。
五、序列化的使用
序列化如何使用?来看一下,序列化的使用的几个关键点吧:

  • 声明一个实体类,实现Serializable接口
  • 使用ObjectOutputStream类的writeObject方法,实现序列化
  • 使用ObjectInputStream类的readObject方法,实现反序列化

声明一个Student类,实现Serializable
public class Student implements Serializable { private Integer age; private String name; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } } 复制代码
使用ObjectOutputStream类的writeObject方法,对Student对象实现序列化
把Student对象设置值后,写入一个文件,即序列化,哈哈~
ObjectOutputStream objectOutputStream = new ObjectOutputStream( new FileOutputStream("D:text.out")); Student student = new Student(); student.setAge(25); student.setName("jayWei"); objectOutputStream.writeObject(student); objectOutputStream.flush(); objectOutputStream.close(); 复制代码
看看序列化的可爱模样吧,test.out文件内容如下(使用UltraEdit打开):

08a9f7848fd97b12a737c0be44c17882.png


使用ObjectInputStream类的readObject方法,实现反序列化,重新生成student对象
再把test.out文件读取出来,反序列化为Student对象
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:text.out")); Student student = (Student) objectInputStream.readObject(); System.out.println("name="+student.getName()); 复制代码

a0dbdd95be217b582c944f1673320f74.png


六、序列化底层
Serializable底层
Serializable接口,只是一个空的接口,没有方法或字段,为什么这么神奇,实现了它就可以让对象序列化了?
public interface Serializable { } 复制代码
为了验证Serializable的作用,把以上demo的Student对象,去掉实现Serializable接口,看序列化过程怎样吧~

86acf7f0d09d5fadc4e2081a24d44f06.png


序列化过程中抛出异常啦,堆栈信息如下:
Exception in thread "main" java.io.NotSerializableException: com.example.demo.Student at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at com.example.demo.Test.main(Test.java:13) 复制代码
顺着堆栈信息看一下,原来有重大发现,如下~

679762db3b57e957689af430f28d9fc4.png

原来底层是这样: ObjectOutputStream 在序列化的时候,会判断被序列化的Object是哪一种类型,String?array?enum?还是 Serializable,如果都不是的话,抛出 NotSerializableException异常。所以呀,Serializable真的只是一个标志,一个序列化标志~
writeObject(Object)
序列化的方法就是writeObject,基于以上的demo,我们来分析一波它的核心方法调用链吧~(建议大家也去debug看一下这个方法,感兴趣的话)

25f9f02569c04b58f0b8f33f9e0900de.png


writeObject直接调用的就是writeObject0()方法,
public final void writeObject(Object obj) throws IOException { ...... writeObject0(obj, false); ...... } 复制代码
writeObject0 主要实现是对象的不同类型,调用不同的方法写入序列化数据,这里面如果对象实现了Serializable接口,就调用writeOrdinaryObject()方法~
private void writeObject0(Object obj, boolean unshared) throws IOException { ...... //String类型 if (obj instanceof String) { writeString((String) obj, unshared); //数组类型 } else if (cl.isArray()) { writeArray(obj, desc, unshared); //枚举类型 } else if (obj instanceof Enum) { writeEnum((Enum<?>) obj, desc, unshared); //Serializable实现序列化接口 } else if (obj instanceof Serializable) { writeOrdinaryObject(obj, desc, unshared); } else{ //其他情况会抛异常~ if (extendedDebugInfo) { throw new NotSerializableException( cl.getName() + "n" + debugInfoStack.toString()); } else { throw new NotSerializableException(cl.getName()); } } ...... 复制代码
writeOrdinaryObject()会先调用writeClassDesc(desc),写入该类的生成信息,然后调用writeSerialData方法,写入序列化数据
private void writeOrdinaryObject(Object obj, ObjectStreamClass desc, boolean unshared) throws IOException { ...... //调用ObjectStreamClass的写入方法 writeClassDesc(desc, false); // 判断是否实现了Externalizable接口 if (desc.isExternalizable() && !desc.isProxy()) { writeExternalData((Externalizable) obj); } else { //写入序列化数据 writeSerialData(obj, desc); } ..... } 复制代码
writeSerialData()实现的就是写入被序列化对象的字段数据
private void writeSerialData(Object obj, ObjectStreamClass desc) throws IOException { for (int i = 0; i < slots.length; i++) { if (slotDesc.hasWriteObjectMethod()) { //如果被序列化的对象自定义实现了writeObject()方法,则执行这个代码块 slotDesc.invokeWriteObject(obj, this); } else { // 调用默认的方法写入实例数据 defaultWriteFields(obj, slotDesc); } } } 复制代码
defaultWriteFields()方法,获取类的基本数据类型数据,直接写入底层字节容器;获取类的obj类型数据,循环递归调用writeObject0()方法,写入数据~
private void defaultWriteFields(Object obj, ObjectStreamClass desc) throws IOException { // 获取类的基本数据类型数据,保存到primVals字节数组 desc.getPrimFieldValues(obj, primVals); //primVals的基本类型数据写到底层字节容器 bout.write(primVals, 0, primDataSize, false); // 获取对应类的所有字段对象 ObjectStreamField[] fields = desc.getFields(false); Object[] objVals = new Object[desc.getNumObjFields()]; int numPrimFields = fields.length - objVals.length; // 获取类的obj类型数据,保存到objVals字节数组 desc.getObjFieldValues(obj, objVals); //对所有Object类型的字段,循环 for (int i = 0; i < objVals.length; i++) { ...... //递归调用writeObject0()方法,写入对应的数据 writeObject0(objVals[i], fields[numPrimFields + i].isUnshared()); ...... } } 复制代码
七、日常开发序列化的一些注意点

  • static静态变量和transient 修饰的字段是不会被序列化的
  • serialVersionUID问题
  • 如果某个序列化类的成员变量是对象类型,则该对象类型的类必须实现序列化
  • 子类实现了序列化,父类没有实现序列化,父类中的字段丢失问题

static静态变量和transient 修饰的字段是不会被序列化的
static静态变量和transient 修饰的字段是不会被序列化的,我们来看例子分析一波~ Student类加了一个类变量gender和一个transient修饰的字段specialty
public class Student implements Serializable { private Integer age; private String name; public static String gender = "男"; transient String specialty = "计算机专业"; public String getSpecialty() { return specialty; } public void setSpecialty(String specialty) { this.specialty = specialty; } @Override public String toString() { return "Student{" +"age=" + age + ", name='" + name + ''' + ", gender='" + gender + ''' + ", specialty='" + specialty + ''' + '}'; } ...... 复制代码
打印学生对象,序列化到文件,接着修改静态变量的值,再反序列化,输出反序列化后的对象~

cce00a2076c5bcc13081c6450cedaedd.png

运行结果:
序列化前Student{age=25, name='jayWei', gender='男', specialty='计算机专业'} 序列化后Student{age=25, name='jayWei', gender='女', specialty='null'} 复制代码
对比结果可以发现:

  • 1)序列化前的静态变量性别明明是‘男’,序列化后再在程序中修改,反序列化后却变成‘女’了,what?显然这个静态属性并没有进行序列化。其实,静态(static)成员变量是属于类级别的,而序列化是针对对象的~所以不能序列化哦
  • 2)经过序列化和反序列化过程后,specialty字段变量值由'计算机专业'变为空了,为什么呢?其实是因为transient关键字,它可以阻止修饰的字段被序列化到文件中,在被反序列化后,transient 字段的值被设为初始值,比如int型的值会被设置为 0,对象型初始值会被设置为null。

serialVersionUID问题
serialVersionUID 表面意思就是序列化版本号ID,其实每一个实现Serializable接口的类,都有一个表示序列化版本标识符的静态变量,或者默认等于1L,或者等于对象的哈希码。
private static final long serialVersionUID = -6384871967268653799L; 复制代码serialVersionUID有什么用?
JAVA序列化的机制是通过判断类的serialVersionUID来验证版本是否一致的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID和本地相应实体类的serialVersionUID进行比较,如果相同,反序列化成功,如果不相同,就抛出InvalidClassException异常。
接下来,我们来验证一下吧,修改一下Student类,再反序列化操作

ab8d832f98dd5b9dee1c5378e80b01f2.png


Exception in thread "main" java.io.InvalidClassException: com.example.demo.Student; local class incompatible: stream classdesc serialVersionUID = 3096644667492403394, local class serialVersionUID = 4429793331949928814 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1876) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1745) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2033) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1567) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427) at com.example.demo.Test.main(Test.java:20) 复制代码
从日志堆栈异常信息可以看到,文件流中的class和当前类路径中的class不同了,它们的serialVersionUID不相同,所以反序列化抛出InvalidClassException异常。那么,如果确实需要修改Student类,又想反序列化成功,怎么办呢?可以手动指定serialVersionUID的值,一般可以设置为1L或者,或者让我们的编辑器IDE生成
private static final long serialVersionUID = -6564022808907262054L; 复制代码
实际上,阿里开发手册,强制要求序列化类新增属性时,不能修改serialVersionUID字段~

c22334a9217e1dcf6493b087a9ba9e52.png


如果某个序列化类的成员变量是对象类型,则该对象类型的类必须实现序列化
给Student类添加一个Teacher类型的成员变量,其中Teacher是没有实现序列化接口的
public class Student implements Serializable { private Integer age; private String name; private Teacher teacher; ... } //Teacher 没有实现 public class Teacher { ...... } 复制代码
序列化运行,就报NotSerializableException异常啦
Exception in thread "main" java.io.NotSerializableException: com.example.demo.Teacher at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at com.example.demo.Test.main(Test.java:16) 复制代码
其实这个可以在上小节的底层源码分析找到答案,一个对象序列化过程,会循环调用它的Object类型字段,递归调用序列化的,也就是说,序列化Student类的时候,会对Teacher类进行序列化,但是对Teacher没有实现序列化接口,因此抛出NotSerializableException异常。所以如果某个实例化类的成员变量是对象类型,则该对象类型的类必须实现序列化

e001d36bd59134fa22c0c3e6825b8d0a.png


子类实现了Serializable,父类没有实现Serializable接口的话,父类不会被序列化。
子类Student实现了Serializable接口,父类User没有实现Serializable接口
//父类实现了Serializable接口 public class Student extends User implements Serializable { private Integer age; private String name; } //父类没有实现Serializable接口 public class User { String userId; } Student student = new Student(); student.setAge(25); student.setName("jayWei"); student.setUserId("1"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:text.out")); objectOutputStream.writeObject(student); objectOutputStream.flush(); objectOutputStream.close(); //反序列化结果 ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:text.out")); Student student1 = (Student) objectInputStream.readObject(); System.out.println(student1.getUserId()); //output /** * null */ 复制代码
从反序列化结果,可以发现,父类属性值丢失了。因此子类实现了Serializable接口,父类没有实现Serializable接口的话,父类不会被序列化。
八、序列化常见面试题

  • 序列化的底层是怎么实现的?
  • 序列化时,如何让某些成员不要序列化?
  • 在 Java 中,Serializable 和 Externalizable 有什么区别
  • serialVersionUID有什么用?
  • 是否可以自定义序列化过程, 或者是否可以覆盖 Java 中的默认序列化过程?
  • 在 Java 序列化期间,哪些变量未序列化?

1.序列化的底层是怎么实现的?
本文第六小节可以回答这个问题,如回答Serializable关键字作用,序列化标志啦,源码中,它的作用啦~还有,可以回答writeObject几个核心方法,如直接写入基本类型,获取obj类型数据,循环递归写入,哈哈~
2.序列化时,如何让某些成员不要序列化?
可以用transient关键字修饰,它可以阻止修饰的字段被序列化到文件中,在被反序列化后,transient 字段的值被设为初始值,比如int型的值会被设置为 0,对象型初始值会被设置为null。
3.在 Java 中,Serializable 和 Externalizable 有什么区别
Externalizable继承了Serializable,给我们提供 writeExternal() 和 readExternal() 方法, 让我们可以控制 Java的序列化机制, 不依赖于Java的默认序列化。正确实现 Externalizable 接口可以显著提高应用程序的性能。
4.serialVersionUID有什么用?
可以看回本文第七小节哈,JAVA序列化的机制是通过判断类的serialVersionUID来验证版本是否一致的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID和本地相应实体类的serialVersionUID进行比较,如果相同,反序列化成功,如果不相同,就抛出InvalidClassException异常。
5.是否可以自定义序列化过程, 或者是否可以覆盖 Java 中的默认序列化过程?
可以的。我们都知道,对于序列化一个对象需调用 ObjectOutputStream.writeObject(saveThisObject), 并用 ObjectInputStream.readObject() 读取对象, 但 Java 虚拟机为你提供的还有一件事, 是定义这两个方法。如果在类中定义这两种方法, 则 JVM 将调用这两种方法, 而不是应用默认序列化机制。同时,可以声明这些方法为私有方法,以避免被继承、重写或重载。
6.在 Java 序列化期间,哪些变量未序列化?
static静态变量和transient 修饰的字段是不会被序列化的。静态(static)成员变量是属于类级别的,而序列化是针对对象的。transient关键字修字段饰,可以阻止该字段被序列化到文件中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/410093.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

decimal类型对象里面定义什么类型_奥斯塔罗 单身开启桃花雷达 现阶段的我适合什么类型的对象?...

相信单身朋友总好奇下一位对象是否出现了&#xff1f;或是这么多人我该如何察觉下一位对象呢&#xff1f;如果单身的你正寻找对象中那就跟奥斯老师一起来看看下一位对象的个性与特征吧&#xff01;&#xff1c;&#xff1c;直觉选一张牌&#xff1e;>牌l选择到这组牌的朋友&…

win7系统怎么拷贝到u盘_Win7系统电脑无法识别U盘启动盘怎么办?

U盘想必大家不会陌生&#xff0c;通常会用来存储一些资料便于移动办公&#xff0c;有些小伙伴也会将其变更成U盘启动盘&#xff0c;帮助电脑重装系统。但是有用户反馈U盘插入电脑后无法识别启动&#xff0c;这要怎么办呢&#xff1f;其实很简单&#xff0c;下面小编就给大家带来…

数据库日志

很多时候需要追踪记录的变更情况&#xff0c;AX里有数据库日志来完成这件事情&#xff0c;操作路径为:系统管理->设置->数据库日志.如果按照向导添加后没看到相应的记录&#xff0c;可以尝试如下方法&#xff0c;选中生命科学电子签名。系统管理->设置->系统->配…

Nginx的应用之安装配置

一、Nginx简述 Nginx是一个开源且高性能、可靠的Http Web服务、代理服务。 开源: 直接获取源代码 高性能: 支持海量并发 可靠: 服务稳定 我们为什么选择 Nginx服务 Nginx非常轻量 功能模块少 (源代码仅保留http与核心模块代码,其余不够核心代码会作为插件来安装) 代码模块化 (易…

包含以下字段的 struct 怎么解决_S7-300/400进阶笔记4-2:STEP7的“结构”STRUCT数据类型的用法...

复杂数据类型是由基本数据类型通过一定的规则&#xff0c;有机结合在一起&#xff0c;形成的新的、复杂数据类型&#xff0c;STEP7中定义了以下几种复合数据类型&#xff1a;本文&#xff0c;我们介绍一下结构——STRUCT。STRUCT表示由一个不同数据类型的数据组合在一起&#x…

springboot a service调用b service_CaaS: 内容是新的基础设施 Content-as-a-Service

内容是每家企业的必争之地&#xff0c;根据 CMI 的数据报告&#xff0c;88% 的 B2B 企业每天至少产生一篇内容。内容正在成为新的基础设施&#xff0c;Content as a Service 可以被简单理解为一种 CMS&#xff08;Content Management Systen&#xff0c;内容管理系统&#xff0…

javascript网页特效_南通建网站哪些,网页设计维护

无锡邦程信息科技有限公司为您详细解读rKXvd南通建网站哪些的相关知识与详情&#xff0c;#末网页设想师根据差别站点的内容微风格&#xff0c;设想出完美的网页效果图。内容填充&#xff1a;不论是个人网站还是企业网站&#xff0c;都必需从内容那个动身点考虑&#xff0c;一个…

request用法_虚拟语气用法总结

虚拟语气主要指的的是带有非真实条件状语从句的复合句&#xff0c;表达的是说话人的遗憾后悔的语气&#xff0c;愿望以及建议&#xff0c;命令&#xff0c;要求等等的情感&#xff0c;往往说的是与事实相反或者没办法实现改变的东西。虚拟语气主要表现为在对过去现在或者将来的…

成为高效程序员的几大搜索技巧

对于缺乏编程知识的人来说&#xff0c;完全有可能编写一个网页或小程序。如果在用Google搜索相关示例时幸运的话&#xff0c;可以搜到现成的代码。即使是经验丰富的程序员&#xff0c;通常也会为了节省时间和精力而在网上搜索解决方案。 如果不借助搜索技术、网络及集体智慧&am…

flink运行原理_Flink运行架构剖析

本文主要介绍 Flink Runtime 的作业执行的核心机制。首先介绍 Flink Runtime 的整体架构以及 Job 的基本执行流程&#xff0c;然后介绍Flink 的Standalone运行架构&#xff0c;最后对Flink on YARN的两种模式进行了详细剖析。Flink Runtime作业执行流程分析整体架构图Flink Run…

vb 获取系统声音的电平_质量好的背景音乐系统套装效果图

笔者是主张低阶设计的&#xff0c;但不一定是越低阶越好&#xff0c;前面已经讲过是以分而治之的手段&#xff0c;达致阴阳融合之目的。分而治之就是把高低音单元的互相不协调的部分和缺点去掉&#xff0c;保留各自的优点&#xff0c;以达到互补融合之目的。有扩声系统中才存在…

vue初级尝试

为了跟上前端后台化的潮流&#xff0c;本少不得不开始关注vue&#xff0c;下列上机代码是针对App.vue进行的更改 数据渲染----一般键值对&#xff0c;数组&#xff0c;对象和对象数组<template><div id"app"><img><h1>{{msg}}</h1><…

钉钉产品介绍_钉钉正式推出智能OA:免费开放、一站解决“人财物事”管理难题...

9月2日消息&#xff0c;阿里钉钉今日正式上线智能OA产品“OA审批”&#xff0c;通过开放工作流、审批流引擎&#xff0c;向中小企业提供场景全面、定制简易、操作门槛低的OA协同服务&#xff0c;实现企业管理流程、业务流程的全链路数字化&#xff0c;一站解决人财物事管理难题…

同方知网软件_国产精品软件天若OCR:支持图像识别/自动翻译/公式识别等

日常生活中有时候我们会遇到某些文件以截图方式发送的 , 想要提取其中的文字内容进行整理就需要靠识别。毕竟如果内容较多的话靠手打可能是个费时费力的事情&#xff0c;OCR识别方面的软件很多但是多数软件都是收费的。在收费软件里自然也有价格便宜和价格比较高的&#xff0c;…

n维椭球体积公式_物质的量浓度公式如何应用

我们常用到的物理量一般有长度、时间、速度等等&#xff0c;物质的量也和这些概念一样&#xff0c;是一个物理量的整体名词&#xff0c;今天小编就为大家介绍一下什么是物质的量&#xff0c;物质的量的浓度公式是什么。1、国际单位制7个物理量长度(单位&#xff1a;m)、质量(单…

Hadoop中RPC机制

Hadoop中RPC机制 RPC(Remote Procedure Call Protocol)远程过程调用协议&#xff0c;它是一种通过网络从远程计算机程序上请求服务&#xff0c;而不需要了解底层网络技术的协议。Hadoop底层的交互都是通过rpc进行的。例如&#xff1a;datanode和namenode 、tasktracker和jobtra…

C++中如何读取一个数的位数_求1000以内的水仙花数

点击上方 蓝字关注我们大家好&#xff0c;我是阿汤哥。看知乎上有朋友说还不明白怎么判断水仙花数&#xff0c;今天我们就来看看这个问题。(PS&#xff1a;“求1000以内的水仙花数”这道题阿汤哥记忆犹新。到现在还记得这是我大一上学期期末考试的编程题。)怎么求水仙花数&…

双向绑定v-bind

通过v-model绑定输出数据<script> export default {data(){return {pagestyle:https://v4.bootcss.com/docs/4.3/dist/css/bootstrap.css,pagecss:https://v4.bootcss.com/docs/4.3/examples/sticky-footer-navbar/sticky-footer-navbar.css,mytitle:v-bind bootstrap样…

xyz坐标图_“色觉地图”的建立(二):辐照度与亮度、rgb空间、“颜色图”的混色方式...

上篇““色觉地图”的建立&#xff08;一&#xff09;&#xff1a;光感受器、色匹配实验与CIE RGB坐标系“中说到&#xff0c;人的色觉是线性的&#xff0c;我们可以用叠加原理“混色”——这意味着色觉的空间固定不变&#xff0c;我们可以任意选择一组基底&#xff08;或说坐标…

Nginx的应用之动静分离

Nginx 的动静分离 我们通过中间件将动态请求和静态请求进行分离&#xff0c;减少了不必要的请求消耗和延时。 动静分离后&#xff0c;即使动态服务不可用&#xff0c;但静态资源不会受到影响。 应用实例 1、准备环境 系统角色主机名IP服务CentOS 7.2反向代理Nginx_Proxy192.168…