两重含义:
1、壳,此时shell是一个用 C 语言编写的应用程,是用户与内核实现交互的窗口
2、一门解释性的脚本编程语言
3、具体的shell源码,常以.sh结尾
最常见的是以下面这个字符串开头:
#!/usr/bin/sh
在 shell 脚本, #! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 解释器。 #! 被称作shebang(也称为 Hashbang )。 所以,你应该会在 shell 中,见到诸如以下的注释:
指定 sh 解释器
#!/bin/sh
指定 bash 解释器
#!/bin/bash
常见的shell如下:
名称 | 描述 |
---|---|
sh | 即 Bourne Shell。sh 是 Unix 标准默认的 shell |
bash | 即 Bourne Again Shell。bash 是 Linux 标准默认的 shell。即bash是sh的扩展,有更强大的语法功能 |
fish | 智能和用户友好的命令行 shell。 |
xiki | 使 shell 控制台更友好,更强大。 |
zsh | 功能强大的 shell 与脚本语言。 |
当你从其它语言转过来的时候,一定会不太适应shell的几个语法坑
在if else语法中,if与后面的表达式必须有空格隔开,这对于熟悉其他语言的程序员来说很容易将其忽略,导致面对语法错误的提示百思不得其解。
if [ expression ]
then
Statement(s) to be executed if expression is true
fi
如果then与if在同一行,则需要分号分割.
if [ expression ];then
Statement(s) to be executed if expression is true
fi
在表达多个元素时,元素之间是采用空格个来分隔,比如:
# 定义数组
array_name=(value0 value1 value2 value3)
#函数参数
printf "%d %s\n" 1 "abc"。
为什么不能使用逗号呢?估计是历史原因吧。
定义变量时,变量名和等号之间不能有空格!!!
myNum =10 # 错误,等号前有空格
myNum=10 # 正确
本文章主要是记录自己初步学习,所以部分资料非本人原创,我会将引用到的相关资料放在最后的
参考资料
中。
Spliterator是一个可分割迭代器(splitable iterator),即可将迭代器进行分割,从则实现并行处理效果,在多核处理器场景下相比串行处理有很大优势。
以ArrayList.stream();为入口
package java.util;
import java.util.function.Consumer;
import java.util.function.DoubleConsumer;
import java.util.function.IntConsumer;
import java.util.function.LongConsumer;
public interface Spliterator<T> {
//通过characteristics()方法返回的值,用来标识实现类所具有的的特征
//表示元素是有序的(每一次遍历结果相同)
public static final int ORDERED = 0x00000010;
//表示元素不重复
public static final int DISTINCT = 0x00000001;
//表示元素是按一定规律进行排列(有指定比较器)
public static final int SORTED = 0x00000004;
//是否确定大小
public static final int SIZED = 0x00000040;
//表示迭代器中没有null元素
public static final int NONNULL = 0x00000100;
//表示元素不可变
public static final int IMMUTABLE = 0x00000400;
//表示迭代器可以多线程操作
public static final int CONCURRENT = 0x00001000;
//表示子Spliterators都具有SIZED特性
public static final int SUBSIZED = 0x00004000;
//如果有剩余的元素存在,执行参数给定的操作,并返回true,否则就返回false。
//如果Spliterator对象具有ORDERED属性,那么tryAdvance也会按照相应的顺序去执行。
boolean tryAdvance(Consumer<? super T> var1);
//对剩余元素执行给定的动作,依次处理,直到所有元素已被处理或被异常终止。默认方法调用tryAdvance方法
default void forEachRemaining(Consumer<? super T> var1) {
while(this.tryAdvance(var1)) {
;
}
}
//对任务分割,返回一个新的Spliterator迭代器
Spliterator<T> trySplit();
//用于估算(estimate)还剩下多少个元素需要遍历
long estimateSize();
//当迭代器拥有SIZED特征时,返回剩余元素个数;否则返回-1
default long getExactSizeIfKnown() {
return (this.characteristics() & 64) == 0 ? -1L : this.estimateSize();
}
//返回当前对象有哪些特征值
int characteristics();
//是否具有当前特征值
default boolean hasCharacteristics(int var1) {
return (this.characteristics() & var1) == var1;
}
//如果Spliterator的list是通过Comparator排序的,则返回Comparator
//如果Spliterator的list是自然排序的 ,则返回null
//其他情况下抛错
default Comparator<? super T> getComparator() {
throw new IllegalStateException();
}
public interface OfDouble extends Spliterator.OfPrimitive<Double, DoubleConsumer, Spliterator.OfDouble> {
...
}
public interface OfInt extends Spliterator.OfPrimitive<Integer, IntConsumer, Spliterator.OfInt> {
...
}
public interface OfLong extends Spliterator.OfPrimitive<Long, LongConsumer, Spliterator.OfLong> {
...
}
public interface OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>> extends Spliterator<T> {
...
}
}
//1、Collection
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
//2、Collection
default Spliterator<E> spliterator() {
//this为当前的ArrayList引用
return Spliterators.spliterator(this, 0);
}
//3、Spliterator工具类Spliterators
public static <T> Spliterator<T> spliterator(Collection<? extends T> c,
int characteristics) {
return new IteratorSpliterator<>(Objects.requireNonNull(c),
characteristics);
}
//IteratorSpliterator为Spliterators内部类,实现了Spliterator
public IteratorSpliterator(Collection<? extends T> collection, int characteristics) {
//保留对集合的引用
this.collection = collection;
this.it = null;
this.characteristics = (characteristics & Spliterator.CONCURRENT) == 0
? characteristics | Spliterator.SIZED | Spliterator.SUBSIZED
: characteristics;
}
一个Stream 包含一个流来源、0 或多个中间操作,以及一个终止操作。
流来源可以是集合、数组、生成器函数或其他任何适当地提供了其元素的访问权的数据源。
中间操作将流转换为其他流 — 通过过滤元素 (filter()
),转换元素 (map()
),排序元素 (sorted()
),将流截断为一定大小 (limit()
),等等。
终止操作包括聚合(reduce()
、collect()
),搜索 (findFirst()
) 和迭代 (forEach()
)。
流管道是惰性构造的。构造流来源不会计算流的元素,而是会确定在必要时如何找到元素。类似地,调用中间操作不会在元素上执行任何计算;只会将另一个操作添加到流描述的末尾。仅在调用终止操作时,管道才会实际执行相应的工作:计算元素,应用中间操作,以及应用终止操作。这种执行方法使得执行多项有趣的优化成为可能。
借助 java.util.stream
包,您可以简明地、声明性地表达集合、数组和其他数据源上可能的并行批量操作。
Stream流水线组织结构示意图如下:
package java.util.stream;
import java.util.Spliterator;
import java.util.function.IntFunction;
import java.util.stream.Node.Builder;
/**
*为了计算,“流”操作组成了一个流管道。一个流管道包括数据源、中间操作和终端操作。数据源可以是数组、集合、*I/O通道和生成函数。而中间操作则是像过滤filter 或者map这种将一个流转换为另一个流的操作。那终端操作呢,就*是产生一个结果或者别的副作用(转为集合或者统计成一个数字)。流是惰性的,源数据的计算只在终端操作启动时操
*作,流只在需要时消费。
* PipelineHelper为管道抽象
**/
abstract class PipelineHelper<P_OUT> {
PipelineHelper() {
}
/**获取输出形状 StreamShape 是个枚举类型:
REFERENCE,INT_VALUE,LONG_VALUE,DOUBLE_VALUE
正常都是引用类型 REFERENCE
*/
abstract StreamShape getSourceShape();
//获取组合标志,包含流标志和操作标志
abstract int getStreamAndOpFlags();
/** 如果已知,则返回将这个 PipelineHelper 所描述的管道阶段应用到提供的Spliterator 所描述的输入部 分所产生的输出部分的精确大小。如果不知道或不知道无穷大,则返回 -1。
确定“知不知道”,则判断提供的Spliterator 是否有特征值SIZED
*/
abstract <P_IN> long exactOutputSizeIfKnown(Spliterator<P_IN> var1);
/**该方法先调用上面的wrapSink 方法,然后使用返回的Sink 处理Spliterator中的数据*/
abstract <P_IN, S extends Sink<P_OUT>> S wrapAndCopyInto(S var1, Spliterator<P_IN> var2);
abstract <P_IN> void copyInto(Sink<P_IN> var1, Spliterator<P_IN> var2);
abstract <P_IN> void copyIntoWithCancel(Sink<P_IN> var1, Spliterator<P_IN> var2);
/**当终端操作初始化的时候调用该方法将所有实现PipelineHelper类型的中间操作包装到给定的Sink中 ,并返回这个Sink*/
abstract <P_IN> Sink<P_IN> wrapSink(Sink<P_OUT> var1);
abstract <P_IN> Spliterator<P_OUT> wrapSpliterator(Spliterator<P_IN> var1);
abstract Builder<P_OUT> makeNodeBuilder(long var1, IntFunction<P_OUT[]> var3);
/** 在遇到终端操作时执行该方法*/
abstract <P_IN> Node<P_OUT> evaluate(Spliterator<P_IN> var1, boolean var2, IntFunction<P_OUT[]> var3);
}
package com.roboslyq.core.common;
public class HelloWorldBean {
public void sayHello(){
System.out.println("hello,world");
}
public void sayHello2(){
System.out.println("hello,world");
}
public void sayHello1(){
System.out.println("hello,world");
}
public void sayHello3(){
System.out.println("hello,world");
}
}
编译就不说了,直接放入IDEA或者javac 都可以获取相应的Class源文件。
这一步有很多方式可以获取,我选择了比较简单的一种,直接使用文件流读取,然后转换成16进制。最后在进行分析。
/**
* Copyright (C), 2015-2019
* FileName: PrintClass
* Author: luo.yongqian
* Date: 2019/4/23 14:38
* Description: 直接使用流打印Class内容
* History:
* <author> <time> <version> <desc>
* luo.yongqian 2019/4/23 14:38 1.0.0 创建
*/
package com.roboslyq.core.bytecode;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
*
* 〈直接使用流打印Class内容〉
* @author luo.yongqian
* @create 2019/4/23
* @since 1.0.0
*/
public class PrintClass {
public static void main(String[] args) throws IOException {
//Class文件保存路径(根据实际情况自己选定)
File file = new File("core\\target\\classes\\com\\roboslyq\\core\\common\\HelloWorldBean.class");
try( FileInputStream isr = new FileInputStream(file);){
byte[] fileContext = isr.readAllBytes();
//此处输入为Class文件原内容(十六进制展示)
System.out.println(str2HexStr(fileContext));
//测试将十六进制转换为普通字符串(翻译),即可以根据需要将读出Class内容进行翻译
//System.out.println(str2HexStr("sayHello2".getBytes()));
}
}
/**
* 将字符串转换为16进制
* @param bs
* @return
*/
private static String str2HexStr(byte[] bs) {
char[] chars = "0123456789ABCDEF".toCharArray();
StringBuilder sb = new StringBuilder("");
int bit;
for (int i = 0; i < bs.length; i++) {
bit = (bs[i] & 0x0f0) >> 4;
sb.append(chars[bit]);
bit = bs[i] & 0x0f;
sb.append(chars[bit]);
}
return sb.toString();
}
/**
* 将十六进制转换成字符串
* @param srcStr
* @return
*/
public static String hexStringToString(String srcStr) {
if (srcStr == null || srcStr.equals("")) {
return null;
}
srcStr = srcStr.replace(" ", "");
byte[] baKeyword = new byte[srcStr.length() / 2];
for (int i = 0; i < baKeyword.length; i++) {
try {
baKeyword[i] = (byte) (0xff & Integer.parseInt(srcStr.substring(i * 2, i * 2 + 2), 16));
} catch (Exception e) {
e.printStackTrace();
}
}
try {
srcStr = new String(baKeyword, StandardCharsets.UTF_8);
} catch (Exception e1) {
e1.printStackTrace();
}
return srcStr;
}
}
打印日志
CAFEBABE0000003500220A0006001409001500160800170A0018001907001A07001B0100063C696E69743E010003282956010004436F646501000F4C696E654E756D6265725461626C650100124C6F63616C5661726961626C655461626C65010004746869730100294C636F6D2F726F626F736C79712F636F72652F636F6D6D6F6E2F48656C6C6F576F726C644265616E3B01000873617948656C6C6F01000973617948656C6C6F3201000973617948656C6C6F3101000973617948656C6C6F3301000A536F7572636546696C6501001348656C6C6F576F726C644265616E2E6A6176610C0007000807001C0C001D001E01000B68656C6C6F2C776F726C6407001F0C00200021010027636F6D2F726F626F736C79712F636F72652F636F6D6D6F6E2F48656C6C6F576F726C644265616E0100106A6176612F6C616E672F4F626A6563740100106A6176612F6C616E672F53797374656D0100036F75740100154C6A6176612F696F2F5072696E7453747265616D3B0100136A6176612F696F2F5072696E7453747265616D0100077072696E746C6E010015284C6A6176612F6C616E672F537472696E673B2956002100050006000000000005000100070008000100090000002F00010001000000052AB70001B100000002000A00000006000100000003000B0000000C000100000005000C000D00000001000E000800010009000000370002000100000009B200021203B60004B100000002000A0000000A00020000000500080006000B0000000C000100000009000C000D00000001000F000800010009000000370002000100000009B200021203B60004B100000002000A0000000A00020000000800080009000B0000000C000100000009000C000D000000010010000800010009000000370002000100000009B200021203B60004B100000002000A0000000A00020000000B0008000C000B0000000C000100000009000C000D000000010011000800010009000000370002000100000009B200021203B60004B100000002000A0000000A00020000000E0008000F000B0000000C000100000009000C000D000000010012000000020013
上面的输出16进制即为Class文件中具体内容。
约定:使用双竖线“ ”进行比较大的模块分割,单竖线“ ”进行一个模块内的单元分割。“*”在模块内进行更好的分割。
||CAFEBABE 魔数
||0000 小版本号
||0035 主版本号
||0022 常量个数
---------------------常量池开始-----------------------
||0A|0006|0014 索引编号:1:0x000A = 10 表示 CONSTANT_methodref_info,
||09|0015|0016 索引编号:2
||08|0017 索引编号:3
||0A|0018|0019 索引编号:4
||07|001A 索引编号:5
||07|001B 索引编号:6:0x0007 = 7,表示CONSTANT_class_info,
||01|0006|3C696E69743E 索引编号:7 :,值为:<init>
||01|0003|282956 索引编号:8 :,值为:()V
||01|0004|436F6465 索引编号:9 :,值为:Code
||01|000F|4C696E654E756D6265725461626C65 索引编号:10 :,值为:LineNumberTable
||01|0012|4C6F63616C5661726961626C655461626C65 索引编号0x0B = 11 ,值为:LocalVariableTable
||01|0004|74686973 索引编号0x0C = 12 ,值为:this
||01|0029|4C636F6D2F726F626F736C79712F636F72652F636F6D6D6F6E2F48656C6C6F576F726C644265616E3B 索引编号0x0D = 13 ,值为:Lcom/roboslyq/core/common/HelloWorldBean;
||01|0008|73617948656C6C6F 索引编号0x0E = 14 ,值为:sayHello
||01|0009|73617948656C6C6F32 索引编号0x0F = 15 ,值为:sayHello2
||01|0009|73617948656C6C6F31 索引编号0x10 = 16 ,值为:sayHello1
||01|0009|73617948656C6C6F33 索引编号0x11 = 17 ,值为:sayHello3
||01|000A|536F7572636546696C65 索引编号0x12 = 18 ,值为:SourceFile
||01|0013|48656C6C6F576F726C644265616E2E6A617661 索引编号0x13 = 19,值为:HelloWorldBean.java
||0C|0007|0008
||07|001C
||0C|001D|001E
||01|000B|68656C6C6F2C776F726C64
||07|001F
||0C|0020|0021
||01|0027|636F6D2F726F626F736C79712F636F72652F636F6D6D6F6E2F48656C6C6F576F726C644265616E 索引编号:0x001A=26,值为com/roboslyq/core/common/HelloWorldBean
||01|0010|6A6176612F6C616E672F4F626A656374 索引编号:0x001A=27,值为java/lang/Object
||01|0010|6A6176612F6C616E672F53797374656D 索引编号:0x001B=28,值为java/lang/System
||01|0003|6F7574
||01|0015|4C6A6176612F696F2F5072696E7453747265616D3B
||01|0013|6A6176612F696F2F5072696E7453747265616D
||01|0007|7072696E746C6E
||01|0015|284C6A6176612F6C616E672F537472696E673B2956
-------------------常量池结束---------------------------------------
||0021 ACC_FLAG:访问标识符(Bit思想)
||0005 类索引在常量池中位置:0005为07类型,故使用值0x001A定位到常量池中值为:com/roboslyq/core/common/HelloWorldBean
||0006 父类索引在常量池中位置:0006为07类型,故使用值0x001B定位到常量池中值为:java/lang/Object,表明父类为Object类。
||0000 接口计数器(表示没有实现接口)
||0000 字段表容量计数器(0x0000 表示没有字段)
||0005 方法表计数器(表明有5个方法:4个方法+1个默认构造函数)
------------------方法1:构造函数方法开始---------------------------------------
||0001 access_flag: 0001,表示public
|0007 name_index: 0007,指向常量池中的#7位置,而#7最终值为"<init>",所以这是个构造方法
|0008 descriptor_index:指向常量池中的#8位置,而#8最终值为"()V",表示返回类型为void
|0001 u2:attributes_count属性个数
------------------构造函数属性表开始-------------------------------------
------------------Code属性表开始----------------------------------------
|0009 u2:attribute_name_index:属性类型常量池索引:0009=#9,指向Code部分,表示接下来是Code的数据结构
|0000002F attribute_length: 0x002F = 47(整个Code属性表长度,一直到文中下面|0000*0005*000C*000D*0000结束,刚好47字节)
|0001 max_stack
|0001 max_locals 局部变量表所需要的存储空间,这里是0001=1
|00000005 code_length:字节码指令的长度,这里是00000005,即5,表示接下来的5个字节(2A-B1)都是字节码指令
|2AB70001B1 code
|0000 exception_table_length 异常表的长度,接下来都是异常表的信息,这里没有定义异常,所以是0000 start_pc->u2(占2个字节):没有定义异常,无
end_pc->u2(占2个字节):没有定义异常,无
handler_pc->u2(占2个字节):没有定义异常,无
catch_pc->u2(占2个字节):没有定义异常,无
|0002 attributes_count
------------------LineNumberTable属性表开始---------------------------------------
|000A U2:attribute_name_index:属性类型常量池索引:指向常量池中#10的位置,为“LineNumberTable”
|00000006 U4:attribute_length:属性长度为6(表示接下来6个字节来表示相关信息)
|0001 U2:Line_Number_table_length:值为1,表示Lint_number_table的长度
|00000003 U2-1:字节码行号 U2-2:源码行号
------------------LineNumberTable属性表结束---------------------------------------
------------------LocalVariableTable属性表开始---------------------------------------
|000B U2:attribute_name_index:属性类型常量池索引:指向常量池中#11的位置,值为:LocalVariableTable
|0000000C U4:attribute_length:属性长度为C
|0001 U2:Local_Variable_table_length:值为1,表示只有一个Local_Variable_info
|0000*0005*000C*000D*0000 Local_Variable_info信息:start_pc*length*name_index(常量池位置)*descript_index(值为Lcom/roboslyq/core/common/HelloWorldBean;)*index
------------------LocalVariableTable属性表结束---------------------------------------
------------------Code属性表结束---------------------------------------
------------------构造函数属性表结束-------------------------------------
------------------方法1:构造函数方法结束---------------------------------------
------------------方法2:sayHello()方法开始---------------------------------------
||0001 0001,表示public
|000E 000E,索引编号0x000E = 14 ,值为sayHello
|0008 指向常量池中的#8位置,而#8最终值为"()V",表示返回类型为void
|0001 属性个数
------------------sayHello()属性表开始-------------------------------------
------------------Code属性表开始----------------------------------------
|0009 属性类型常量池索引:0x0009=#9,指向Code部分,表示接下来是Code的数据结构
|00000037 整个属性表长度 0x0037 = 3*16 + 7 = 55
|0002
|0001
|00000009
|B200021203B60004B1
|0000
|0002 Code属性表属性个数 attributes_count
------------------LineNumberTable属性表开始---------------------------------------
|000A
|0000000A
|0002 U2:Line_Number_table_length:值为2,表示Lint_number_table的长度
|00000005
|00080006
------------------LineNumberTable属性表结束---------------------------------------
------------------LocalVariableTable属性表开始---------------------------------------
|000B U2:attribute_name_index:属性类型常量池索引:指向常量池中#11的位置,值为:LocalVariableTable
|0000000C U4:attribute_length:属性长度为C
|0001 U2:Local_Variable_table_length:值为1,表示只有一个Local_Variable_info
|0000*0009*000C*000D*0000 Local_Variable_info信息:start_pc*length*name_index(常量池位置)*descript_index(值为Lcom/roboslyq/core/common/HelloWorldBean;)*index
------------------LocalVariableTable属性表结束---------------------------------------
------------------Code属性表结束---------------------------------------
------------------构造函数属性表结束-------------------------------------
------------------方法2:sayHello()方法结束---------------------------------------
------------------方法3:sayHello2()方法开始---------------------------------------
||0001
|000F
|0008
|0001
------------------sayHello2()属性表开始-------------------------------------
------------------Code属性表开始----------------------------------------
|0009
|00000037
|0002
|0001
|00000009
|B200021203B60004B1
|0000
|0002
------------------LineNumberTable属性表开始---------------------------------------
|000A
|0000000A
|0002
|00000008
|00080009
------------------LineNumberTable属性表结束---------------------------------------
------------------LocalVariableTable属性表开始---------------------------------------
|000B
|0000000C
|0001
|0000*0009*000C*000D*0000
------------------LocalVariableTable属性表结束---------------------------------------
------------------Code属性表结束---------------------------------------
------------------构造函数属性表结束-------------------------------------
------------------方法3:sayHello2()方法结束---------------------------------------
------------------方法4:sayHello1()方法开始---------------------------------------
||0001
|0010 索引编号0x0E = 16 ,值为:sayHello1
|0008
|0001
------------------sayHello1()属性表开始-------------------------------------
------------------Code属性表开始----------------------------------------
|0009
|00000037
|0002
|0001
|00000009
|B200021203B60004B1
|0000
|0002
------------------LineNumberTable属性表开始---------------------------------------
|000A
|0000000A
|0002
|0000000B
|0008000C
------------------LineNumberTable属性表结束---------------------------------------
------------------LocalVariableTable属性表开始---------------------------------------
|000B
|0000000C
|0001
|0000*0009*000C*000D*0000
------------------LocalVariableTable属性表结束---------------------------------------
------------------Code属性表结束---------------------------------------
------------------构造函数属性表结束-------------------------------------
------------------方法4:sayHello1()方法结束---------------------------------------
------------------方法5:sayHello3()方法开始---------------------------------------
||0001
|0011 索引编号0x11 = 17 ,值为:sayHello3
|0008
|0001
------------------sayHello3()属性表开始-------------------------------------
------------------Code属性表开始----------------------------------------
|0009
|00000037
|0002
|0001
|00000009
|B200021203B60004B1
|0000
|0002
------------------LineNumberTable属性表开始---------------------------------------
|000A
|0000000A
|0002
|0000000E
|0008000F
------------------LineNumberTable属性表结束---------------------------------------
------------------LocalVariableTable属性表开始---------------------------------------
|000B
|0000000C
|0001
|0000*0009*000C*000D*0000
------------------LocalVariableTable属性表结束---------------------------------------
------------------Code属性表结束---------------------------------------
------------------构造函数属性表结束-------------------------------------
------------------方法5:sayHello3()方法结束---------------------------------------
00010012000000020013
山外山,天外天,自己自是一个普通的程序员。要自信不要自大。
思想,是编程的核心。
而知识的积累是思想的源泉。
脚踏实地,做好技术。