首页 文章 精选 留言 我的

精选列表

搜索[基础搭建],共10000篇文章
优秀的个人博客,低调大师

阿里云轻量应用服务器基础建站系统部署Web环境与应用

如果我们开始就接触阿里云主机产品的话,可能认识其中的ECS服务器和虚拟主机,目前又出来轻量应用服务器,这里我们不去分析其产品背后的配置以及架构,单单从产品本身应用上看轻量应用服务器比较偏向于建站用户的。比如在安装系统的时候可以选择无环境的系统镜像,也可以选择安装LAMP、WordPress、宝塔面板等环境自带系统。 其实在ECS服务器中也是可以安装的,直接在云市场中选择安装。老左不清楚阿里云出这款产品的目的是什么。其次,在轻量应用服务器面板中功能上比ECS服务器稍微简单一些。包括常规的系统安装、安全组、快照、磁盘管理。在这篇文章中,老左看看其自带的应用镜像如何操作和管理的。有需要的朋友可以提前领取优惠劵 第一、安装应用镜像 这里老左选择安装应用镜像,如果是安装系统镜像与我们熟悉的ECS没有什么区别。这里应用镜像包括直接安装WP、LAMP、Node.js、ECSHOP等常规CMS的直接安装。但是这里我还是安装宝塔面板。(看来宝塔最近几年发展还可以,都和云主机商捆绑 合作了) 这里看到我手上这测试机在重置安装。如果我们有重要数据需要提前备份,因为重新安装系统后已有数据是没有了的。 第二、设置防火墙安全组 这里在安装完毕之后发现原来系统的ROOT还是可以使用的,系统账户没有变,然后系统是安装了宝塔面板。当然,原来的数据也是没有了的。这里我们可能要想着去登录宝塔面板(因为这里我是安装的这个),但是无法登录。因为没有设置安全组将8888端口添加进去。 这里我们参考"阿里云轻量应用服务器设置防火墙规则放行端口方法"文章将需要的8888以及888端口添加进来。 第三、初始化面板账户 这里我们需要先登录SSH,或者是远程连接,输入命令初始化面板的密码。 这里可以看到我们的宝塔面板密码。复制密码,然后正常的去登录面板初始化。 看到这里是不是熟悉了?选择安装LNMP还是LAMP就可以了。完毕之后,就一个宝塔面板安装在阿里轻量应用服务器中,就如同我们在ECS服务器中安装一样。 第四、小结 1、阿里云轻量应用服务器相对ECS服务器而言,可视化界面操作比较多一些。(这里不去分析机器的区别)可以直接安装需要的镜像,以及可视化设置账户。 2、我们如果不喜欢自带镜像也可以自己安装一个精简版的系统,然后类似ECS服务器去部署。我们不要忽视安全组,如果不设置的话是无法访问的。同时还有快照工具可以使用。轻量服务器上的快照备份是免费的(目前是),快照可备份服务器数据,总快照数量上限为已创建实例数乘以3,但最多不超过15个。对于ECS服务器上快照是收费的。了解更多详细内容可点击查看哦

优秀的个人博客,低调大师

夯实Java基础系列22:一文读懂Java序列化和反序列化

本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下Star哈 文章首发于我的个人博客: www.how2playlife.com 本文参考 http://www.importnew.com/17964.html和https://www.ibm.com/developerworks/cn/java/j-lo-serial/ 序列化与反序列化概念 序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等。在网络传输过程中,可以是字节或是XML等格式。而字节的或XML编码格式可以还原完全相等的对象。这个相反的过程又称为反序列化。 Java对象的序列化与反序列化 在Java中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用该对象。但是,我们创建出来的这些Java对象都是存在于JVM的堆内存中的。 只有JVM处于运行状态的时候,这些对象才可能存在。一旦JVM停止运行,这些对象的状态也就随之而丢失了。 但是在真实的应用场景中,我们需要将这些对象持久化下来,并且能够在需要的时候把对象重新读取出来。Java的对象序列化可以帮助我们实现该功能。 对象序列化机制(object serialization)是Java语言内建的一种对象持久化方式,通过对象序列化,可以把对象的状态保存为字节数组,并且可以在有需要的时候将这个字节数组通过反序列化的方式再转换成对象。 对象序列化可以很容易的在JVM中的活动对象和字节数组(流)之间进行转换。 在Java中,对象的序列化与反序列化被广泛应用到RMI(远程方法调用)及网络传输中。 相关接口及类 Java为了方便开发人员将Java对象进行序列化及反序列化提供了一套方便的API来支持。其中包括以下接口和类: java.io.Serializable java.io.Externalizable ObjectOutput ObjectInput ObjectOutputStream ObjectInputStream Serializable 接口 类通过实现 java.io.Serializable 接口以启用其序列化功能。 未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。 (该接口并没有方法和字段,为什么只有实现了该接口的类的对象才能被序列化呢?) 当试图对一个对象进行序列化的时候,如果遇到不支持 Serializable 接口的对象。在此情况下,将抛出NotSerializableException。 如果要序列化的类有父类,要想同时将在父类中定义过的变量持久化下来,那么父类也应该集成java.io.Serializable接口。 下面是一个实现了java.io.Serializable接口的类 public class 序列化和反序列化 { public static void main(String[] args) { } //注意,内部类不能进行序列化,因为它依赖于外部类 @Test public void test() throws IOException { A a = new A(); a.i = 1; a.s = "a"; FileOutputStream fileOutputStream = null; FileInputStream fileInputStream = null; try { //将obj写入文件 fileOutputStream = new FileOutputStream("temp"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(a); fileOutputStream.close(); //通过文件读取obj fileInputStream = new FileInputStream("temp"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); A a2 = (A) objectInputStream.readObject(); fileInputStream.close(); System.out.println(a2.i); System.out.println(a2.s); //打印结果和序列化之前相同 } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } class A implements Serializable { int i; String s; } Externalizable接口 除了Serializable 之外,java中还提供了另一个序列化接口Externalizable 为了了解Externalizable接口和Serializable接口的区别,先来看代码,我们把上面的代码改成使用Externalizable的形式。 class B implements Externalizable { //必须要有公开无参构造函数。否则报错 public B() { } int i; String s; @Override public void writeExternal(ObjectOutput out) throws IOException { } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { } } @Test public void test2() throws IOException, ClassNotFoundException { B b = new B(); b.i = 1; b.s = "a"; //将obj写入文件 FileOutputStream fileOutputStream = new FileOutputStream("temp"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(b); fileOutputStream.close(); //通过文件读取obj FileInputStream fileInputStream = new FileInputStream("temp"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); B b2 = (B) objectInputStream.readObject(); fileInputStream.close(); System.out.println(b2.i); System.out.println(b2.s); //打印结果为0和null,即初始值,没有被赋值 //0 //null } 通过上面的实例可以发现,对B类进行序列化及反序列化之后得到的对象的所有属性的值都变成了默认值。也就是说,之前的那个对象的状态并没有被持久化下来。这就是Externalizable接口和Serializable接口的区别: Externalizable继承了Serializable,该接口中定义了两个抽象方法:writeExternal()与readExternal()。 当使用Externalizable接口来进行序列化与反序列化的时候需要开发人员重写writeExternal()与readExternal()方法。由于上面的代码中,并没有在这两个方法中定义序列化实现细节,所以输出的内容为空。 还有一点值得注意:在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。所以,实现Externalizable接口的类必须要提供一个public的无参的构造器。 class C implements Externalizable { int i; int j; String s; public C() { } //实现下面两个方法可以选择序列化中需要被复制的成员。 //并且,写入顺序和读取顺序要一致,否则报错。 //可以写入多个同类型变量,顺序保持一致即可。 @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(i); out.writeInt(j); out.writeObject(s); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { i = in.readInt(); j = in.readInt(); s = (String) in.readObject(); } } @Test public void test3() throws IOException, ClassNotFoundException { C c = new C(); c.i = 1; c.j = 2; c.s = "a"; //将obj写入文件 FileOutputStream fileOutputStream = new FileOutputStream("temp"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(c); fileOutputStream.close(); //通过文件读取obj FileInputStream fileInputStream = new FileInputStream("temp"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); C c2 = (C) objectInputStream.readObject(); fileInputStream.close(); System.out.println(c2.i); System.out.println(c2.j); System.out.println(c2.s); //打印结果为0和null,即初始值,没有被赋值 //0 //null } 序列化ID 序列化 ID 问题情境:两个客户端 A 和 B 试图通过网络传递对象数据,A 端将对象 C 序列化为二进制数据再传给 B,B 反序列化得到 C。 问题:C 对象的全类路径假设为 com.inout.Test,在 A 和 B 端都有这么一个类文件,功能代码完全一致。也都实现了 Serializable 接口,但是反序列化时总是提示不成功。 解决:虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。清单 1 中,虽然两个类的功能代码完全一致,但是序列化 ID 不同,他们无法相互序列化和反序列化。 package com.inout; import java.io.Serializable; public class A implements Serializable { private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } package com.inout; import java.io.Serializable; public class A implements Serializable { private static final long serialVersionUID = 2L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } 静态变量不参与序列化 清单 2 中的 main 方法,将对象序列化后,修改静态变量的数值,再将序列化对象读取出来,然后通过读取出来的对象获得静态变量的数值并打印出来。依照清单 2,这个 System.out.println(t.staticVar) 语句输出的是 10 还是 5 呢? public class Test implements Serializable { private static final long serialVersionUID = 1L; public static int staticVar = 5; public static void main(String[] args) { try { //初始时staticVar为5 ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("result.obj")); out.writeObject(new Test()); out.close(); //序列化后修改为10 Test.staticVar = 10; ObjectInputStream oin = new ObjectInputStream(new FileInputStream( "result.obj")); Test t = (Test) oin.readObject(); oin.close(); //再读取,通过t.staticVar打印新的值 System.out.println(t.staticVar); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } 最后的输出是 10,对于无法理解的读者认为,打印的 staticVar 是从读取的对象里获得的,应该是保存时的状态才对。之所以打印 10 的原因在于序列化时,并不保存静态变量,这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。 探究ArrayList的序列化 ArrayList的序列化在介绍ArrayList序列化之前,先来考虑一个问题: 如何自定义的序列化和反序列化策略 带着这个问题,我们来看java.util.ArrayList的源码 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8683452581122892189L; transient Object[] elementData; // non-private to simplify nested class access private int size; } 笔者省略了其他成员变量,从上面的代码中可以知道ArrayList实现了java.io.Serializable接口,那么我们就可以对它进行序列化及反序列化。 因为elementData是transient的(1.8好像改掉了这一点),所以我们认为这个成员变量不会被序列化而保留下来。我们写一个Demo,验证一下我们的想法: public class ArrayList的序列化 { public static void main(String[] args) throws IOException, ClassNotFoundException { ArrayList list = new ArrayList(); list.add("a"); list.add("b"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("arr")); objectOutputStream.writeObject(list); objectOutputStream.close(); ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("arr")); ArrayList list1 = (ArrayList) objectInputStream.readObject(); objectInputStream.close(); System.out.println(Arrays.toString(list.toArray())); //序列化成功,里面的元素保持不变。 } 了解ArrayList的人都知道,ArrayList底层是通过数组实现的。那么数组elementData其实就是用来保存列表中的元素的。通过该属性的声明方式我们知道,他是无法通过序列化持久化下来的。那么为什么code 4的结果却通过序列化和反序列化把List中的元素保留下来了呢? writeObject和readObject方法 在ArrayList中定义了来个方法: writeObject和readObject。 这里先给出结论: 在序列化过程中,如果被序列化的类中定义了writeObject 和 readObject 方法,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化。 如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。 用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。 来看一下这两个方法的具体实现: private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { elementData = EMPTY_ELEMENTDATA; // Read in size, and any hidden stuff s.defaultReadObject(); // Read in capacity s.readInt(); // ignored if (size > 0) { // be like clone(), allocate array based upon size not capacity ensureCapacityInternal(size); Object[] a = elementData; // Read in all elements in the proper order. for (int i=0; i<size; i++) { a[i] = s.readObject(); } } } private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out size as capacity for behavioural compatibility with clone() s.writeInt(size); // Write out all elements in the proper order. for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } 那么为什么ArrayList要用这种方式来实现序列化呢? why transient ArrayList实际上是动态数组,每次在放满以后自动增长设定的长度值,如果数组自动增长长度设为100,而实际只放了一个元素,那就会序列化99个null元素。为了保证在序列化的时候不会将这么多null同时进行序列化,ArrayList把元素数组设置为transient。 why writeObject and readObject 前面说过,为了防止一个包含大量空对象的数组被序列化,为了优化存储,所以,ArrayList使用transient来声明elementData。 但是,作为一个集合,在序列化过程中还必须保证其中的元素可以被持久化下来,所以,通过重写writeObject 和 readObject方法的方式把其中的元素保留下来。 writeObject方法把elementData数组中的元素遍历的保存到输出流(ObjectOutputStream)中。 readObject方法从输入流(ObjectInputStream)中读出对象并保存赋值到elementData数组中。 如何自定义的序列化和反序列化策略 延续上一部分,刚刚我们明白了ArrayList序列化数组元素的原理。 至此,我们先试着来回答刚刚提出的问题: 如何自定义的序列化和反序列化策略 答:可以通过在被序列化的类中增加writeObject 和 readObject方法。那么问题又来了: 虽然ArrayList中写了writeObject 和 readObject 方法,但是这两个方法并没有显示的被调用啊。 那么如果一个类中包含writeObject 和 readObject 方法,那么这两个方法是怎么被调用的呢? ObjectOutputStream从code 4中,我们可以看出,对象的序列化过程通过ObjectOutputStream和ObjectInputputStream来实现的,那么带着刚刚的问题,我们来分析一下ArrayList中的writeObject 和 readObject 方法到底是如何被调用的呢? 为了节省篇幅,这里给出ObjectOutputStream的writeObject的调用栈: writeObject ---> writeObject0 --->writeOrdinaryObject--->writeSerialData--->invokeWriteObject 这里看一下invokeWriteObject: void invokeWriteObject(Object obj, ObjectOutputStream out) throws IOException, UnsupportedOperationException { if (writeObjectMethod != null) { try { writeObjectMethod.invoke(obj, new Object[]{ out }); } catch (InvocationTargetException ex) { Throwable th = ex.getTargetException(); if (th instanceof IOException) { throw (IOException) th; } else { throwMiscException(th); } } catch (IllegalAccessException ex) { // should not occur, as access checks have been suppressed throw new InternalError(ex); } } else { throw new UnsupportedOperationException(); } } 其中writeObjectMethod.invoke(obj, new Object[]{ out });是关键,通过反射的方式调用writeObjectMethod方法。官方是这么解释这个writeObjectMethod的: class-defined writeObject method, or null if none 在我们的例子中,这个方法就是我们在ArrayList中定义的writeObject方法。通过反射的方式被调用了。 至此,我们先试着来回答刚刚提出的问题: 如果一个类中包含writeObject 和 readObject 方法,那么这两个方法是怎么被调用的? 答:在使用ObjectOutputStream的writeObject方法和ObjectInputStream的readObject方法时,会通过反射的方式调用。 为什么要实现Serializable 至此,我们已经介绍完了ArrayList的序列化方式。那么,不知道有没有人提出这样的疑问: Serializable明明就是一个空的接口,它是怎么保证只有实现了该接口的方法才能进行序列化与反序列化的呢? Serializable接口的定义: public interface Serializable { } 读者可以尝试把code 1中的继承Serializable的代码去掉,再执行code 2,会抛出java.io.NotSerializableException。 其实这个问题也很好回答,我们再回到刚刚ObjectOutputStream的writeObject的调用栈: writeObject ---> writeObject0 --->writeOrdinaryObject--->writeSerialData--->invokeWriteObject writeObject0方法中有这么一段代码: 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); } 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()); } } 在进行序列化操作时,会判断要被序列化的类是否是Enum、Array和Serializable类型,如果不是则直接抛出NotSerializableException。 序列化知识点总结 1、如果一个类想被序列化,需要实现Serializable接口。否则将抛出NotSerializableException异常,这是因为,在序列化操作过程中会对类型进行检查,要求被序列化的类必须属于Enum、Array和Serializable类型其中的任何一种。 2、通过ObjectOutputStream和ObjectInputStream对对象进行序列化及反序列化 3、虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID) 序列化 ID 在 Eclipse 下提供了两种生成策略,一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具生成),在这里有一个建议,如果没有特殊需求,就是用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。那么随机生成的序列化 ID 有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。 4、序列化并不保存静态变量。 5、要想将父类对象也序列化,就需要让父类也实现Serializable 接口。 6、Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。 7、服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的密钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化对象的数据安全。 8、在类中增加writeObject 和 readObject 方法可以实现自定义序列化策略 参考文章 https://blog.csdn.net/qq_34988624/article/details/86592229 https://www.meiwen.com.cn/subject/slhvhqtx.html https://blog.csdn.net/qq_34988624/article/details/86592229 https://segmentfault.com/a/1190000012220863 https://my.oschina.net/wuxinshui/blog/1511484 https://blog.csdn.net/hukailee/article/details/81107412 微信公众号 Java技术江湖 如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号【Java技术江湖】一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM、SpringBoot、MySQL、分布式、中间件、集群、Linux、网络、多线程,偶尔讲点Docker、ELK,同时也分享技术干货和学习经验,致力于Java全栈开发! Java工程师必备学习资源: 一些Java工程师常用学习资源,关注公众号后,后台回复关键字 “Java” 即可免费无套路获取。 个人公众号:黄小斜 作者是跨考软件工程的 985 硕士,自学 Java 两年,拿到了 BAT 等近十家大厂 offer,从技术小白成长为阿里工程师。 作者专注于 JAVA 后端技术栈,热衷于分享程序员干货、学习经验、求职心得和程序人生,目前黄小斜的CSDN博客有百万+访问量,知乎粉丝2W+,全网已有10W+读者。 黄小斜是一个斜杠青年,坚持学习和写作,相信终身学习的力量,希望和更多的程序员交朋友,一起进步和成长! 关注公众号【黄小斜】后回复【原创电子书】即可领取我原创的电子书《菜鸟程序员修炼手册:从技术小白到阿里巴巴Java工程师》 程序员3T技术学习资源: 一些程序员学习技术的资源大礼包,关注公众号后,后台回复关键字 “资料” 即可免费无套路获取。

优秀的个人博客,低调大师

Python零基础学习代码实践 —— 打印5位数的回文数并统计个数

代码: num = 10000 sum = 0 while num <= 99999: a = num % 10 b = num // 10000 c = num % 1000 // 10 d = (num - (num//10000)*10000)//1000 if a == b and c == d: print(num) sum += 1 num += 1 print("5位数的回文数个数为:"sum) 执行结果: 10001 11011 12021 13031 14041 15051 16061 17071 18081 19091 20002 21012 22022 23032 24042 25052 26062 27072 28082 29092 30003 31013 32023 33033 34043 35053 36063 37073 38083 39093 40004 41014 42024 43034 44044 45054 46064 47074 48084 49094 50005 51015 52025 53035 54045 55055 56065 57075 58085 59095 60006 61016 62026 63036 64046 65056 66066 67076 68086 69096 70007 71017 72027 73037 74047 75057 76067 77077 78087 79097 80008 81018 82028 83038 84048 85058 86068 87078 88088 89098 90009 91019 92029 93039 94049 95059 96069 97079 98089 99099 5位数的回文数个数为: 90

优秀的个人博客,低调大师

1024程序员 | 我如何从零基础转行成为一个自信的前端

1 来自未来的补充:原标题是“学前端,我如何在一年内学到三年的知识”。经网友批评和自己反思后,决定改一个合适点的标题。另外,有不少朋友以为我在国外,其实我一直在深圳。目前我也在想进入技术好一点的团队,可打杂。 这个大言不惭的标题源自我与我所认识的前端从业者的比较,也源自别人的评价。读者也可以看我其它文章,评估我的专业能力。当然我不是在每个领域都能匹配别人三年的实践经验。我还有很多技术栈没掌握,比如没写过小程序,没用过 jQuery(这个也不想再学了,只是依然还有用人单位在要求)。也有很多坑没踩过,比如没有做过浏览器兼容(说实话也不太想兼容 IE,浪费生命)。我想说的是我对 JS 这门语言以及 CS 这门学科掌握的深度,前端生态圈理解和熟悉的程度,以及知识迁移能力。 我不是想炫耀自己多牛,而是想帮助和我有相似背景的人。我在掘金发了几篇文章

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册