范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

为了搞清楚Java类加载,回家手撸JVM

  作者:小傅哥
  博客:https://bugstack.cn
  沉淀、分享、成长,让自己和他人都能有所收获! 一、前言
  学习,不知道从哪下手?
  当学习一个新知识不知道从哪下手的时候,最有效的办法是梳理这个知识结构的脉络信息,汇总出一整张的思维导出。接下来就是按照思维导图的知识结构,一个个学习相应的知识点,并汇总记录。
  就像JVM的学习,可以说它包括了非常多的内容,也是一个庞大的知识体系。例如: 类加载 、加载器 、生命周期 、性能优化 、调优参数 、调优工具 、优化方案 、内存区域 、虚拟机栈 、直接内存 、内存溢出 、元空间 、垃圾回收 、可达性分析 、标记清除 、回收过程 等等。如果没有梳理的一头扎进去,东一榔头西一棒子,很容易造成学习恐惧感。
  如图 24-1 是 JVM 知识框架梳理,后续我们会按照这个结构陆续讲解每一块内容。
  图 24-1 JVM 知识框架 二、面试题
  谢飞机,小记! ,很多知识根本就是背背背,也没法操作,难学!
  谢飞机 :大哥,你问我两个JVM问题,我看看我自己还行不!
  面试官 :啊?嗯!往死了问还是?
  谢飞机 :就就就,都行!你看着来!
  面试官 :啊,那 JVM 加载过程都是什么步骤?
  谢飞机 :巴拉巴拉,加载、验证、准备、解析、初始化、使用、卸载!
  面试官 :嗯,背的挺好! 我怀疑你没操作过!  那加载的时候,JVM 规范规定从第几位开始是解析常量池,以及数据类型是如何定义的,u1、u2、u4,是怎么个玩意?
  谢飞机 :握草!算了,告诉我看啥吧! 三、类加载过程描述
  图 24-2 JVM 类加载过程
  JVM 类加载过程分为 , 加载 、链接 、初始化 、使用 和卸载 这五个阶段,在链接中又包括:验证 、准备 、解析 。加载 :Java 虚拟机规范对 class 文件格式进行了严格的规则,但对于从哪里加载 class 文件,却非常自由。Java 虚拟机实现可以从文件系统读取、从JAR(或ZIP)压缩包中提取 class 文件。除此之外也可以通过网络下载、数据库加载,甚至是运行时直接生成的 class 文件。链接 :包括了三个阶段; 验证,确保被加载类的正确性,验证字节流是否符合 class 文件规范,例魔数 0xCAFEBABE,以及版本号等。准备,为类的静态变量分配内存并设置变量初始值等解析,解析包括解析出常量池数据和属性表信息,这里会包括 ConstantPool 结构体以及 AttributeInfo 接口等。 初始化 :类加载完成的最后一步就是初始化,目的就是为标记常量值的字段赋值,以及执行   方法的过程。JVM虚拟机通过锁的方式确保 clinit 仅被执行一次 使用 :程序代码执行使用阶段。卸载 :程序代码退出、异常、结束等。四、写个代码加载下
  JVM 之所以不好掌握,主要是因为不好实操。虚拟机是 C++ 写的,很多 Java 程序员根本就不会去读,或者读不懂。那么,也就没办法实实在在的体会到,到底是怎么加载的,加载的时候都干了啥。 只有看到代码,我才觉得自己学会了!
  所以,我们这里要手动写一下,JVM 虚拟机的部分代码,也就是类加载的过程。通过 Java 代码来实现 Java 虚拟机的部分功能,让开发 Java 代码的程序员更容易理解虚拟机的执行过程。 1. 案例工程interview-24 ├── pom.xml └── src     └── main     │    └── java     │        └── org.itstack.interview.jvm     │             ├── classpath     │             │   ├── impl     │             │   │   ├── CompositeEntry.java     │             │   │   ├── DirEntry.java      │             │   │   ├── WildcardEntry.java      │             │   │   └── ZipEntry.java         │             │   ├── Classpath.java     │             │   └── Entry.java         │             ├── Cmd.java     │             └── Main.java     └── test          └── java              └── org.itstack.interview.jvm.test                  └── HelloWorld.java
  以上 ,工程结构就是按照 JVM 虚拟机规范,使用 Java 代码实现 JVM 中加载 class 文件部分内容。当然这部分还不包括解析,因为解析部分的代码非常庞大,我们先从把 .class 文件加载读取开始了解。 2. 代码讲解2.1 定义类路径接口(Entry)public interface Entry {      byte[] readClass(String className) throws IOException;          static Entry create(String path) {         //File.pathSeparator;路径分隔符(winlinux)         if (path.contains(File.pathSeparator)) {             return new CompositeEntry(path);         }         if (path.endsWith("*")) {             return new WildcardEntry(path);         }         if (path.endsWith(".jar") || path.endsWith(".JAR") ||                 path.endsWith(".zip") || path.endsWith(".ZIP")) {             return new ZipEntry(path);         }         return new DirEntry(path);     } } 接口中提供了接口方法  readClass  和静态方法 create(String path) 。jdk1.8 是可以在接口中编写静态方法的,在设计上属于补全了抽象类的类似功能。这个静态方法主要是按照不同的路径地址类型,提供不同的解析方法。包括:CompositeEntry、WildcardEntry、ZipEntry、DirEntry,这四种。 接下来分别看每一种的具体实现 2.2 目录形式路径(DirEntry)public class DirEntry implements Entry {      private Path absolutePath;      public DirEntry(String path){         //获取绝对路径         this.absolutePath = Paths.get(path).toAbsolutePath();     }      @Override     public byte[] readClass(String className) throws IOException {         return Files.readAllBytes(absolutePath.resolve(className));     }      @Override     public String toString() {         return this.absolutePath.toString();     } } 目录形式的通过读取绝对路径下的文件,通过  Files.readAllBytes  方式获取字节码。2.3 压缩包形式路径(ZipEntry)public class ZipEntry implements Entry {      private Path absolutePath;      public ZipEntry(String path) {         //获取绝对路径         this.absolutePath = Paths.get(path).toAbsolutePath();     }      @Override     public byte[] readClass(String className) throws IOException {         try (FileSystem zipFs = FileSystems.newFileSystem(absolutePath, null)) {             return Files.readAllBytes(zipFs.getPath(className));         }     }      @Override     public String toString() {         return this.absolutePath.toString();     }  } 其实压缩包形式与目录形式,只有在文件读取上有包装差别而已。 FileSystems.newFileSystem 2.4 混合形式路径(CompositeEntry)public class CompositeEntry implements Entry {      private final List entryList = new ArrayList<>();      public CompositeEntry(String pathList) {         String[] paths = pathList.split(File.pathSeparator);         for (String path : paths) {             entryList.add(Entry.create(path));         }     }      @Override     public byte[] readClass(String className) throws IOException {         for (Entry entry : entryList) {             try {                 return entry.readClass(className);             } catch (Exception ignored) {                 //ignored             }         }         throw new IOException("class not found " + className);     }       @Override     public String toString() {         String[] strs = new String[entryList.size()];         for (int i = 0; i < entryList.size(); i++) {             strs[i] = entryList.get(i).toString();         }         return String.join(File.pathSeparator, strs);     }      } File.pathSeparator ,是一个分隔符属性,win/linux 有不同的类型,所以使用这个方法进行分割路径。分割后的路径装到 List 集合中,这个过程属于拆分路径。 2.5 通配符类型路径(WildcardEntry)public class WildcardEntry extends CompositeEntry {      public WildcardEntry(String path) {         super(toPathList(path));     }      private static String toPathList(String wildcardPath) {         String baseDir = wildcardPath.replace("*", ""); // remove *         try {             return Files.walk(Paths.get(baseDir))                     .filter(Files::isRegularFile)                     .map(Path::toString)                     .filter(p -> p.endsWith(".jar") || p.endsWith(".JAR"))                     .collect(Collectors.joining(File.pathSeparator));         } catch (IOException e) {             return "";         }     }  } 这个类属于混合形式路径处理类的子类,唯一提供的方法就是把类路径解析出来。 2.6 类路径解析(Classpath)
  启动类路径 、扩展类路径 、用户类路径 ,熟悉吗?是不经常看到这几句话,那么时候怎么实现的呢?
  有了上面我们做的一些基础类的工作,接下来就是类解析的实际调用过程。代码如下: public class Classpath {      private Entry bootstrapClasspath;  //启动类路径     private Entry extensionClasspath;  //扩展类路径     private Entry userClasspath;       //用户类路径      public Classpath(String jreOption, String cpOption) {         //启动类&扩展类 "C:Program FilesJavajdk1.8.0_161jre"         bootstrapAndExtensionClasspath(jreOption);         //用户类 F:..orgitstackdemo	estHelloWorld         parseUserClasspath(cpOption);     }      private void bootstrapAndExtensionClasspath(String jreOption) {                  String jreDir = getJreDir(jreOption);          //..jre/lib/*         String jreLibPath = Paths.get(jreDir, "lib") + File.separator + "*";         bootstrapClasspath = new WildcardEntry(jreLibPath);          //..jre/lib/ext/*         String jreExtPath = Paths.get(jreDir, "lib", "ext") + File.separator + "*";         extensionClasspath = new WildcardEntry(jreExtPath);      }      private static String getJreDir(String jreOption) {         if (jreOption != null && Files.exists(Paths.get(jreOption))) {             return jreOption;         }         if (Files.exists(Paths.get("./jre"))) {             return "./jre";         }         String jh = System.getenv("JAVA_HOME");         if (jh != null) {             return Paths.get(jh, "jre").toString();         }         throw new RuntimeException("Can not find JRE folder!");     }      private void parseUserClasspath(String cpOption) {         if (cpOption == null) {             cpOption = ".";         }         userClasspath = Entry.create(cpOption);     }      public byte[] readClass(String className) throws Exception {         className = className + ".class";          //[readClass]启动类路径         try {             return bootstrapClasspath.readClass(className);         } catch (Exception ignored) {             //ignored         }          //[readClass]扩展类路径         try {             return extensionClasspath.readClass(className);         } catch (Exception ignored) {             //ignored         }          //[readClass]用户类路径         return userClasspath.readClass(className);     }  } 启动类路径,bootstrapClasspath.readClass(className); 扩展类路径,extensionClasspath.readClass(className); 用户类路径,userClasspath.readClass(className); 这回就看到它们具体在哪使用了吧!有了具体的代码也就方便理解了 2.7 加载类测试验证private static void startJVM(Cmd cmd) {     Classpath cp = new Classpath(cmd.jre, cmd.classpath);     System.out.printf("classpath:%s class:%s args:%s ", cp, cmd.getMainClass(), cmd.getAppArgs());     //获取className     String className = cmd.getMainClass().replace(".", "/");     try {         byte[] classData = cp.readClass(className);         System.out.println(Arrays.toString(classData));     } catch (Exception e) {         System.out.println("Could not find or load main class " + cmd.getMainClass());         e.printStackTrace();     } }
  这段就是使用 Classpath 类进行类路径加载,这里我们测试加载 java.lang.String 类。 你可以加载其他的类,或者自己写的类 配置IDEA,program arguments 参数: -Xjre "C:Program FilesJavajdk1.8.0_161jre" java.lang.String 另外这里读取出的 class 文件信息,打印的是 byte 类型信息。
  测试结果 [-54, -2, -70, -66, 0, 0, 0, 52, 2, 28, 3, 0, 0, -40, 0, 3, 0, 0, -37, -1, 3, 0, 0, -33, -1, 3, 0, 1, 0, 0, 8, 0, 15, 8, 0, 61, 8, 0, 85, 8, 0, 88, 8, 0, 89, 8, 0, 112, 8, 0, -81, 8, 0, -75, 8, 0, -47, 8, 0, -45, 1, 0, 0, 1, 0, 3, 40, 41, 73, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 3, 40, 41, 86, 1, 0, 3, 40, 41, 90, 1, 0, 4, 40, 41, 91, ...]
  这块部分截取的程序运行打印结果,就是读取的 class 文件信息,只不过暂时还不能看出什么。 接下来我们再把它翻译过来! 五、解析字节码文件
  JVM 在把 class 文件加载完成后,接下来就进入 链接 的过程,这个过程包括了内容的校验、准备和解析,其实就是把 byte 类型 class 翻译过来,做相应的操作。
  整个这个过程内容相对较多,这里只做部分逻辑的实现和讲解。如果读者感兴趣可以阅读小傅哥的《用Java实现JVM》专栏。 1. 提取部分字节码//取部分字节码:java.lang.String private static byte[] classData = {         -54, -2, -70, -66, 0, 0, 0, 52, 2, 26, 3, 0, 0, -40, 0, 3, 0, 0, -37, -1, 3, 0, 0, -33, -1, 3, 0, 1, 0, 0, 8, 0,         59, 8, 0, 83, 8, 0, 86, 8, 0, 87, 8, 0, 110, 8, 0, -83, 8, 0, -77, 8, 0, -49, 8, 0, -47, 1, 0, 3, 40, 41, 73, 1,         0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 1, 0, 20, 40, 41,         76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 3, 40, 41, 86, 1, 0, 3,         40, 41, 90, 1, 0, 4, 40, 41, 91, 66, 1, 0, 4, 40, 41, 91, 67, 1, 0, 4, 40, 67, 41, 67, 1, 0, 21, 40, 68, 41, 76,         106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 4, 40, 73, 41, 67, 1, 0, 4}; java.lang.String 解析出来的字节码内容较多,当然包括的内容也多,比如魔数、版本、类、常量、方法等等。所以我们这里只截取部分进行进行解析。 2. 解析魔数并校验
  很多文件格式都会规定满足该格式的文件必须以某几个固定字节开头,这几个字节主要起到标识作用,叫作魔数(magic number)。
  例如; PDF文件以4字节"%PDF"(0x25、0x50、0x44、0x46)开头, ZIP文件以2字节"PK"(0x50、0x4B)开头 class文件以4字节"0xCAFEBABE"开头 private static void readAndCheckMagic() {     System.out.println("r ------------ 校验魔数 ------------");     //从class字节码中读取前四位     byte[] magic_byte = new byte[4];     System.arraycopy(classData, 0, magic_byte, 0, 4);          //将4位byte字节转成16进制字符串     String magic_hex_str = new BigInteger(1, magic_byte).toString(16);     System.out.println("magic_hex_str:" + magic_hex_str);          //byte_magic_str 是16进制的字符串,cafebabe,因为java中没有无符号整型,所以如果想要无符号只能放到更高位中     long magic_unsigned_int32 = Long.parseLong(magic_hex_str, 16);     System.out.println("magic_unsigned_int32:" + magic_unsigned_int32);          //魔数比对,一种通过字符串比对,另外一种使用假设的无符号16进制比较。如果使用无符号比较需要将0xCAFEBABE & 0x0FFFFFFFFL与运算     System.out.println("0xCAFEBABE & 0x0FFFFFFFFL:" + (0xCAFEBABE & 0x0FFFFFFFFL));          if (magic_unsigned_int32 == (0xCAFEBABE & 0x0FFFFFFFFL)) {         System.out.println("class字节码魔数无符号16进制数值一致校验通过");     } else {         System.out.println("class字节码魔数无符号16进制数值一致校验拒绝");     } } 读取字节码中的前四位, -54, -2, -70, -66 ,将这四位转换为16进制。因为 java 中是没有无符号整型的,所以只能用更高位存放。 解析后就是魔数的对比,看是否与 CAFEBABE 一致。
  测试结果 ------------ 校验魔数 ------------ magic_hex_str:cafebabe magic_unsigned_int32:3405691582 0xCAFEBABE & 0x0FFFFFFFFL:3405691582 class字节码魔数无符号16进制数值一致校验通过 3. 解析版本号信息
  刚才我们已经读取了4位魔数信息,接下来再读取2位,是版本信息。
  魔数之后是class文件的次版本号和主版本号,都是u2类型。假设某class文件的主版本号是M,次版本号是m,那么完整的版本号可以表示成"M.m"的形式。次版本号只在J2SE 1.2之前用过,从1.2开始基本上就没有什么用了(都是0)。主版本号在J2SE 1.2之前是45,从1.2开始,每次有大版本的Java版本发布,都会加1{45、46、47、48、49、50、51、52} private static void readAndCheckVersion() {     System.out.println("r ------------ 校验版本号 ------------");     //从class字节码第4位开始读取,读取2位     byte[] minor_byte = new byte[2];     System.arraycopy(classData, 4, minor_byte, 0, 2);          //将2位byte字节转成16进制字符串     String minor_hex_str = new BigInteger(1, minor_byte).toString(16);     System.out.println("minor_hex_str:" + minor_hex_str);          //minor_unsigned_int32 转成无符号16进制     int minor_unsigned_int32 = Integer.parseInt(minor_hex_str, 16);     System.out.println("minor_unsigned_int32:" + minor_unsigned_int32);          //从class字节码第6位开始读取,读取2位     byte[] major_byte = new byte[2];     System.arraycopy(classData, 6, major_byte, 0, 2);          //将2位byte字节转成16进制字符串     String major_hex_str = new BigInteger(1, major_byte).toString(16);     System.out.println("major_hex_str:" + major_hex_str);          //major_unsigned_int32 转成无符号16进制     int major_unsigned_int32 = Integer.parseInt(major_hex_str, 16);     System.out.println("major_unsigned_int32:" + major_unsigned_int32);     System.out.println("版本号:" + major_unsigned_int32 + "." + minor_unsigned_int32); } 这里有一个小技巧,class 文件解析出来是一整片的内容,JVM 需要按照虚拟机规范,一段一段的解析出所有的信息。 同样这里我们需要把2位byte转换为16进制信息,并继续从第6位继续读取2位信息。组合出来的才是版本信息。
  测试结果 ------------ 校验版本号 ------------ minor_hex_str:0 minor_unsigned_int32:0 major_hex_str:34 major_unsigned_int32:52 版本号:52.0 4. 解析全部内容对照
  按照 JVM 的加载过程,其实远不止魔数和版本号信息,还有很多其他内容,这里我们可以把测试结果展示出来,方便大家有一个学习结果的比对印象。 classpath:org.itstack.demo.jvm.classpath.Classpath@4bf558aa class:java.lang.String args:null version: 52.0 constants count:540 access flags:0x31 this class:java/lang/String super class:java/lang/Object interfaces:[java/io/Serializable, java/lang/Comparable, java/lang/CharSequence] fields count:5 value    [C hash    I serialVersionUID    J serialPersistentFields    [Ljava/io/ObjectStreamField; CASE_INSENSITIVE_ORDER    Ljava/util/Comparator; methods count: 94     ()V     (Ljava/lang/String;)V     ([C)V     ([CII)V     ([III)V     ([BIII)V     ([BI)V checkBounds    ([BII)V     ([BIILjava/lang/String;)V     ([BIILjava/nio/charset/Charset;)V     ([BLjava/lang/String;)V     ([BLjava/nio/charset/Charset;)V     ([BII)V     ([B)V     (Ljava/lang/StringBuffer;)V     (Ljava/lang/StringBuilder;)V     ([CZ)V length    ()I isEmpty    ()Z charAt    (I)C codePointAt    (I)I codePointBefore    (I)I codePointCount    (II)I offsetByCodePoints    (II)I getChars    ([CI)V getChars    (II[CI)V getBytes    (II[BI)V getBytes    (Ljava/lang/String;)[B getBytes    (Ljava/nio/charset/Charset;)[B getBytes    ()[B equals    (Ljava/lang/Object;)Z contentEquals    (Ljava/lang/StringBuffer;)Z nonSyncContentEquals    (Ljava/lang/AbstractStringBuilder;)Z contentEquals    (Ljava/lang/CharSequence;)Z equalsIgnoreCase    (Ljava/lang/String;)Z compareTo    (Ljava/lang/String;)I compareToIgnoreCase    (Ljava/lang/String;)I regionMatches    (ILjava/lang/String;II)Z regionMatches    (ZILjava/lang/String;II)Z startsWith    (Ljava/lang/String;I)Z startsWith    (Ljava/lang/String;)Z endsWith    (Ljava/lang/String;)Z hashCode    ()I indexOf    (I)I indexOf    (II)I indexOfSupplementary    (II)I lastIndexOf    (I)I lastIndexOf    (II)I lastIndexOfSupplementary    (II)I indexOf    (Ljava/lang/String;)I indexOf    (Ljava/lang/String;I)I indexOf    ([CIILjava/lang/String;I)I indexOf    ([CII[CIII)I lastIndexOf    (Ljava/lang/String;)I lastIndexOf    (Ljava/lang/String;I)I lastIndexOf    ([CIILjava/lang/String;I)I lastIndexOf    ([CII[CIII)I substring    (I)Ljava/lang/String; substring    (II)Ljava/lang/String; subSequence    (II)Ljava/lang/CharSequence; concat    (Ljava/lang/String;)Ljava/lang/String; replace    (CC)Ljava/lang/String; matches    (Ljava/lang/String;)Z contains    (Ljava/lang/CharSequence;)Z replaceFirst    (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; replaceAll    (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; replace    (Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String; split    (Ljava/lang/String;I)[Ljava/lang/String; split    (Ljava/lang/String;)[Ljava/lang/String; join    (Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String; join    (Ljava/lang/CharSequence;Ljava/lang/Iterable;)Ljava/lang/String; toLowerCase    (Ljava/util/Locale;)Ljava/lang/String; toLowerCase    ()Ljava/lang/String; toUpperCase    (Ljava/util/Locale;)Ljava/lang/String; toUpperCase    ()Ljava/lang/String; trim    ()Ljava/lang/String; toString    ()Ljava/lang/String; toCharArray    ()[C format    (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String; format    (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String; valueOf    (Ljava/lang/Object;)Ljava/lang/String; valueOf    ([C)Ljava/lang/String; valueOf    ([CII)Ljava/lang/String; copyValueOf    ([CII)Ljava/lang/String; copyValueOf    ([C)Ljava/lang/String; valueOf    (Z)Ljava/lang/String; valueOf    (C)Ljava/lang/String; valueOf    (I)Ljava/lang/String; valueOf    (J)Ljava/lang/String; valueOf    (F)Ljava/lang/String; valueOf    (D)Ljava/lang/String; intern    ()Ljava/lang/String; compareTo    (Ljava/lang/Object;)I     ()V  Process finished with exit code 0 如果大家对这部分验证、准备、解析,的实现过程感兴趣,可以参照这部分用Java实现的JVM源码:https://github.com/fuzhengwei/itstack-demo-jvm 六、总结学习 JVM 最大的问题是不好实践,所以本文以案例实操的方式,学习 JVM 的加载解析过程。也让更多的对 JVM 感兴趣的研发,能更好的接触到 JVM 并深入的学习。 有了以上这段代码,大家可以参照 JVM 虚拟机规范,在调试Java版本的JVM,这样就可以非常容易理解整个JVM的加载过程,都做了什么。 如果大家需要文章中一些原图 xmind 或者源码,可以添加作者小傅哥(fustack),或者关注公众号:bugstack虫洞栈进行获取。好了,本章节就扯到这,后续还有很多努力,持续原创,感谢大家的支持!

中储粮提价不限蛋白含量收购国产大豆,业内这释放了一个积极信号受去年大豆上市以来市场价持续低迷的影响,农户收益受损,种大豆积极性不高。在这样的背景下,国家开始推动大豆政策性收储。3月9月,中储粮集团公司在官方网站发布公告,按照国家有关部门安排海通国际小作文空袭次高端白酒引舍得酒业股价闪崩连夜澄清海通国际(00665。HK),一家专注港股市场的券商,近年来对A股白酒很关注,先是海通国际董事总经理闻宏伟携一众美女同事2月底应邀出席在海口召开的2022酒业创新与投资大会,并与复关系中,我劝做个冷淡的人人际关系中,有一个特别有意思的现象,越是不断付出且不求回报的人,越难以收获他人对自己的珍视。相反,那些在关系中比较在意自己感受的人,反而更容易收获他人对自己的好感。究其根源,正是讨与花草为伍,与文字为友,把日子过成诗三月中的凌晨,窗外开始有了婉转的鸟鸣声,清脆而悠长的,千转百回,此起彼伏。像啄醒黎明一样,这种优美的声音也会恰逢其时的啄醒我一觉醒来时常常无望和颓废的日子。风推小轩窗,春事惹青黄,关于人生,你有没有长期规划?你的生活丰富多彩吗?物质丰富吗,你是不是也为生计辗转反侧,生活拮据吗?对于养家糊口的中年人,没有特别容易的生活,你觉得准吗?赚钱表面上缺的是物质金钱,本质上缺的是思想观念,改变一下打豆浆时,黄豆不要直接打,牢记多做3步,细腻丝滑,香浓无腥味打豆浆,黄豆不要直接打,牢记多做3步,细腻丝滑,清甜香浓无腥味豆浆是中国的传统饮品,口感细腻丝滑,味道香甜,有浓浓的豆香味,特别好喝。豆浆的营养也十分丰富,含有植物蛋白和磷脂维生素舌尖上的恩施(恩施州十大名吃)5恩施州十大名吃最后一期为大家带来的是福宝山莼菜和年肉,作者码字不易,喜欢的话各位还是点点关注。9。福宝山莼菜莼菜又名马蹄菜湖菜等,是多年生水生宿根草本。性喜温暖,适宜于无污染的清水四川火锅底料配方火锅老油炼制菜籽油18斤,精炼牛油二斤,郫县红油豆瓣酱750克,汉源红花椒500克,冰糖50克,糍粑辣椒1200克,二荆条辣椒面,100克(糍粑辣椒比例二荆条,辣椒700克,灯笼椒3月14日凌晨,中国传来32个新消息,专家强烈建议恢复农民农业税3月14日凌晨,中国传来32个新消息,专家强烈建议恢复农民农业税!32经济专家张捷提议让农民继续缴纳农业税,网友请你放过农民吧!对此,你们大家怎么看?网友1同意让张捷教授承包上一个泰伦卢谈弃用威少,投篮差,总失误,直言詹姆斯给过自己2个建议威斯布鲁克加盟快船队之后一度让球队遭遇了5连败,但是最近一段时间球队终于是经过磨合之后重回正轨,目前快船队在击败了尼克斯之后,已经拿到了一波三连胜,这样的情况让很多快船球迷都觉得很全国政协委员戴斌建议进一步增加外国游客过境免签地区政协委员在这里封面新闻记者粟裕3月10日,全国两会正在召开,如何促进旅游市场加快复苏,代表委员们在会上建言献策,相关话题热度也持续攀升。全国政协委员中国旅游研究院院长戴斌接受封面新闻专访时建议,
固底板扬优势在发展中保障和改善民生央视网消息党的二十大报告指出,治国有常,利民为本。为民造福是立党为公执政为民的本质要求。必须坚持在发展中保障和改善民生,鼓励共同奋斗创造美好生活,不断实现人民对美好生活的向往。代表手机创新被锁死,缺的不是乔布斯,而是下一个爱因斯坦一部手机中至少有8项诺贝尔奖作者闫学功封面来源摄图网科幻小说三体中,外星人的高级AI智子利用自己的能力干扰人类的物理学实验结果,从而锁死了基础科学的发展,导致一群科学家信仰崩塌。自瑞士9月手表出口猛增,全年总出口额或创历史新高瑞士钟表业联合会10月20日发布声明称,今年9月瑞士手表出口额飙升19。1至22亿瑞士法郎(约合22亿美元),是有史以来表现最好的月份之一。今年前9个月手表出口额达到181亿法郎,犹太人说给自己留条后路,驰骋商界的犹太民族又是怎样做到的呢?犹太商人有一句名言不要把所有的鸡蛋放在同一个篮子里。这是犹太商人分散风险的重要法则,即采取防患于未然的经营策略,量力而行,要最大限度地预防降低商业风险,避免遭遇一损俱损的投资后果。南非游记(5)非洲小瑞士奈斯纳小镇如诗如画的奈斯纳湖昨天晚餐之后,我们离开摩梭湾,前往奈斯纳荒野海滩酒店(WildernessBeachHotel)。荒野海滩酒店是一家SPA水疗酒店,餐厅露台以及所有客房的阳台均享有印度洋美景。赶到风景不输瑞士!西藏这个5A级景点,去过的都说忘不了陈鹏程摄西藏一直以来都是人们梦寐以求的远方可是有很多想来西藏的朋友由于西藏海拔高,含氧量低想要出发却迟迟不敢行动只能在网上看照片羡慕别人自己却只能望而却步张静摄其实,在西藏有这样一爱因斯坦又对了!50亿光年外,星系在引力透镜下一分为二综述到目前为止,人类在各领域的探索已经有了初步的进展,对于自然规律也有了较为详细的了解,并且能够运用这些自然规律,来解释存在于地球上的未知奥秘。但实际上,现在我们所得到的很多结论和矮小的苏麻喇姑墓弯腰才能进,墓中一口缸揭开康熙百年谎言在封建时代,皇权意味着一切,但是天下的皇帝只有一个,所以很多人为了自己的发展与家族的发展,都成了皇权的附庸。他们汲汲营营,只求皇帝能够信赖青睐他们,由此他们能够获得权力的一杯羹。不骨传导耳机天花板?南卡RunnerPro4体验这次真不漏音在近两年的蓝牙耳机市场中,骨传导耳机得到高速发展,逐渐被大众所接纳和认可,凭借着出众的运动性和佩戴体验,成为运动爱好者运动时的最佳伴侣。不过,目前市面上骨传导耳机品牌众多,难免会在桂东交警大队为患病辅警捐款献爱心红网时刻新闻10月22日讯(通讯员李安)10月20日上午,桂东县公安局交警大队举行为患病战友朱延武同志献爱心捐款活动,大队60余名民警辅警参加捐款活动,为困难战友献出一份爱心,送上请人喝酒,支支吾吾讲不好开场白?学会3句话,令人刮目相看老话说无酒不成席!现在不管办什么事,都离不开喝酒,所以对成年人来说,去参加大大小小的酒局更是家常便饭。说到酒局,笔者前两天就去参加了一个同事组的局,在酒桌上大家都起哄让他讲两句,结