博客

  • 2019-10-23-Elasticsearch

    Elasticsearch Rest High Level Client

    昨天折腾了一下Elasticsearch, 今天把过程记录下来免得忘记。

    首先当然是安装,我用的mac, 所以安装很简单:

    brew tap elastic/tap
    brew install elastic/tap/elasticsearch-full
    elasticsearch

    搞定。

    然后就是按照官方的教程,
    用curl把这几个页面上(index,
    search, aggregations)的命令跑了一遍。
    为了免得以后重新打命令,我把这个命令都放到一个reference.sh里面去了,想要跑哪一行,就把那个命令前面的#去掉,然后bash reference.sh就可以了。

    最后还折腾了一下官方的Java REST Client 7.4, 其中有一个坑就是Maven Repository页面上写的是7.4.1,但是我在pom.xml里面用7.4.1死活编译不了,我还以为是因为我用了阿里云的maven源,里面没有更新,换成官方的源还是不行,我去官方的源上一看,到今天(2019-10-23)为止, 官方源上最新的版本是7.4.0,所以上面应该写7.4.0.

    其它的就没什么了,先搞个project:

    mvn -B archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=com.laogongshuo.es -DartifactId=elasticsearch
    cd elasticsearch

    然后把依赖关系加到pom.xml里面去:

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.4.0</version>
        </dependency>
    </dependencies>

    在src/main/java/com/laogongshuo/es/App.java里面撸一些代码:

    package com.laogongshuo.es;
    
    import java.io.IOException;
    import org.apache.http.HttpHost;
    import org.elasticsearch.action.search.SearchRequest;
    import org.elasticsearch.action.search.SearchResponse;
    import org.elasticsearch.client.RequestOptions;
    import org.elasticsearch.client.RestClient;
    import org.elasticsearch.client.RestHighLevelClient;
    import org.elasticsearch.index.query.QueryBuilders;
    import org.elasticsearch.search.SearchHit;
    import org.elasticsearch.search.builder.SearchSourceBuilder;
    
    public class App {
    
      private static final String[] FETCH_FIELDS = {
        "account_number",
        "balance",
        "firstname",
        "lastname",
        "age",
        "gender",
        "address",
        "employer",
        "email",
        "city",
        "state"
      };
    
      public static void matchAll() throws IOException {
        RestHighLevelClient client =
            new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
        SearchRequest searchRequest = new SearchRequest("bank");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        searchRequest.source(searchSourceBuilder);
    
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        if (searchResponse.getHits().getTotalHits().value > 0) {
          System.out.println(searchResponse.getHits().getTotalHits());
          for (SearchHit hit : searchResponse.getHits()) {
            System.out.println("Match: ");
            for (String fetchField : FETCH_FIELDS) {
              System.out.println(" - " + fetchField + " " + hit.getSourceAsMap().get(fetchField));
            }
          }
        } else {
          System.out.println("No results matching the criteria.");
        }
        client.close();
      }
    
      public static void search() throws IOException {
        RestHighLevelClient client =
            new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
        SearchRequest searchRequest = new SearchRequest("bank");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.termQuery("account_number", "49"));
        searchRequest.source(sourceBuilder);
    
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        if (searchResponse.getHits().getTotalHits().value > 0) {
          System.out.println(searchResponse.getHits().getTotalHits());
          for (SearchHit hit : searchResponse.getHits()) {
            System.out.println("Match: ");
            for (String fetchField : FETCH_FIELDS) {
              System.out.println(" - " + fetchField + " " + hit.getSourceAsMap().get(fetchField));
            }
          }
        } else {
          System.out.println("No results matching the criteria.");
        }
        client.close();
      }
    
      public static void main(String[] args) throws IOException {
        matchAll();
        search();
      }
    }

    最后编译运行:

    mvn clean package
    mvn exec:java -Dexec.mainClass="com.laogongshuo.es.App"

    因为没有在pom.xml里面写执行的mainClass,所以只能这么跑,不过能看到结果就行了。

    文中全部代码都在这里,github, 欢迎star.

  • 2019-10-20-学习java最少必要知识

    看了半个月的java, 总结了一份学习java最少必要知识。

    github链接在这里

    Java是由Sun Microsystems公司于1995年5月推出的一种跨平台的,面向对象的通用编程语言。

    随着Java的发展,SUN给Java又分出了三个不同版本:
    Java SE:Standard Edition
    Java EE:Enterprise Edition
    Java ME:Micro Edition
    简单来说,Java SE就是标准版,包含标准的JVM和标准库,而Java EE是企业版,它只是在Java SE的基础上加上了大量的API和库,以便方便开发Web应用、数据库、消息服务等,Java EE的应用使用的虚拟机和Java SE完全相同。
    Java ME就和Java SE不同,它是一个针对嵌入式设备的“瘦身版”,Java SE的标准库无法在Java ME上使用,Java ME的虚拟机也是“瘦身版”。
    毫无疑问,Java SE是整个Java平台的核心,而Java EE是进一步学习Web应用所必须的。我们熟悉的Spring等框架都是Java EE开源生态系统的一部分。不幸的是,Java ME从来没有真正流行起来,反而是Android开发成为了移动平台的标准之一,因此,没有特殊需求,不建议学习Java ME。

    JDK:Java Development Kit
    JRE:Java Runtime Environment
    简单地说,JRE就是运行Java字节码的虚拟机。但是,如果只有Java源码,要编译成Java字节码,就需要JDK,因为JDK除了包含JRE,还提供了编译器、调试器等开发工具。

    JSR规范:Java Specification Request
    JCP组织:Java Community Process
    为了保证Java语言的规范性,SUN公司搞了一个JSR规范,凡是想给Java平台加一个功能,比如说访问数据库的功能,大家要先创建一个JSR规范,定义好接口,这样,各个数据库厂商都按照规范写出Java驱动程序,开发者就不用担心自己写的数据库代码在MySQL上能跑,却不能跑在PostgreSQL上。

    Java基础语法
    一个 Java程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。
    对象:对象是类的一个实例,有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
    类:类是一个模板,它描述一类对象的行为和状态。
    方法:方法就是行为,一个类可以有很多方法。逻辑运算、数据修改以及所有动作都是在方法中完成的。
    实例变量:每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定。

    大小写敏感:Java是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的。
    类名:对于所有的类来说,类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写,例如 MyFirstJavaClass 。
    方法名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。
    源文件名:源文件名必须和类名相同。当保存文件的时候,你应该使用类名作为文件名保存(切记 Java 是大小写敏感的),文件名的后缀为 .java。(如果文件名和类名不相同则会导致编译错误)。
    主方法入口:所有的 Java 程序由 public static void main(String []args) 方法开始执行。

    Java标识符
    所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线()开始 首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线()或数字的任何字符组合
    关键字不能用作标识符
    标识符是大小写敏感的
    合法标识符举例:age、$salary、_value、__1_value
    非法标识符举例:123abc、-salary

    Java修饰符
    访问控制修饰符 : default, public , protected, private
    非访问控制修饰符 : final, abstract, static, synchronized

    Java数组
    数组是储存在堆上的对象,可以保存多个同类型变量。

    Java枚举
    枚举限制变量只能是预先设定好的值。使用枚举可以减少代码中的 bug。

    Java 关键字
    访问控制:
    private 私有的
    protected 受保护的
    public 公共的
    类、方法和变量修饰符:
    abstract 声明抽象
    class 类
    extends 扩充,继承
    final 最终值,不可改变的
    implements 实现(接口)
    interface 接口
    native 本地,原生方法(非 Java 实现)
    new 新,创建
    static 静态
    strictfp 严格,精准
    synchronized 线程,同步
    transient 短暂
    volatile 易失
    程序控制语句:
    break 跳出循环
    case 定义一个值以供 switch 选择
    continue 继续
    default 默认
    do 运行
    else 否则
    for 循环
    if 如果
    instanceof 实例
    return 返回
    switch 根据值选择执行
    while 循环
    错误处理:
    assert 断言表达式是否为真
    catch 捕捉异常
    finally 有没有异常都执行
    throw 抛出一个异常对象
    throws 声明一个异常可能被抛出
    try 捕获异常
    包相关:
    import 引入
    package 包
    基本类型:
    boolean 布尔型
    byte 字节型
    char 字符型
    double 双精度浮点
    float 单精度浮点
    int 整型
    long 长整型
    short 短整型
    变量引用:
    super 父类,超类
    this 本类
    void 无返回值
    保留关键字:
    goto 是关键字,但不能使用
    const 是关键字,但不能使用
    null 空

    Java注释
    Java支持单行以及多行注释。注释中的字符将被 Java 编译器忽略。
    // 这是单行注释的示例
    /* 这个也是单行注释的示例 / / 这是第一个Java程序
    *它将打印Hello World
    * 这是一个多行注释的示例
    */

    Java空行
    空白行或者有注释的行,Java 编译器都会忽略掉。

    继承
    在 Java 中,一个类可以由其他类派生。如果你要创建一个类,而且已经存在一个类具有你所需要的属性或方法,那么你可以将新创建的类继承该类。
    利用继承的方法,可以重用已存在类的方法和属性,而不用重写这些代码。被继承的类称为超类(super class),派生类称为子类(subclass)。

    接口
    在 Java 中,接口可理解为对象间相互通信的协议。接口在继承中扮演着很重要的角色。
    接口只定义派生要用到的方法,但是方法的具体实现完全取决于派生类。

    Java 对象和类
    Java作为一种面向对象语言。支持以下基本概念:
    多态,继承,封装,抽象,类,对象,实例,方法,重载
    对象:对象是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
    类:类是一个模板,它描述一类对象的行为和状态。
    男孩(boy)、女孩(girl)为类(class),而具体的每个人为该类的对象(object)。

    一个类可以包含以下类型变量:
    局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
    成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
    类变量:类变量也声明在类中,方法体之外,但必须声明为static类型。

    构造方法
    每个类都有构造方法。如果没有显式地为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。
    在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。

    创建对象
    对象是根据类创建的。在Java中,使用关键字new来创建一个新的对象。创建对象需要以下三步:
    声明:声明一个对象,包括对象名称和对象类型。
    实例化:使用关键字new来创建一个对象。
    初始化:使用new创建对象时,会调用构造方法初始化对象。

    public class Puppy{
       public Puppy(String name){
          //这个构造器仅有一个参数:name
          System.out.println("小狗的名字是 : " + name );
       }
       public static void main(String[] args){
          // 下面的语句将创建一个Puppy对象
          Puppy myPuppy = new Puppy( "tommy" );
       }
    }

    访问实例变量和方法
    通过已创建的对象来访问成员变量和成员方法,如下所示:

    /* 实例化对象 */
    Object referenceVariable = new Constructor();
    /* 访问类中的变量 */
    referenceVariable.variableName;
    /* 访问类中的方法 */
    referenceVariable.methodName();

    源文件声明规则
    一个源文件中只能有一个public类
    一个源文件可以有多个非public类
    源文件的名称应该和public类的类名保持一致。例如:源文件中public类的类名是Employee,那么源文件应该命名为Employee.java。
    如果一个类定义在某个包中,那么package语句应该在源文件的首行。
    如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。
    import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。

    Java包
    包主要用来对类和接口进行分类。当开发Java程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。
    Import语句
    在Java中,如果给出一个完整的限定名,包括包名、类名,那么Java编译器就可以很容易地定位到源代码或者类。Import语句就是用来提供一个合理的路径,使得编译器可以找到某个类。
    例如,下面的命令行将会命令编译器载入java_installation/java/io路径下的所有类
    import java.io.*;

    Java 基本数据类型
    变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。
    内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。

    Java 的两大数据类型:
    内置数据类型
    引用数据类型

    内置数据类型
    Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
    byte:
    byte 数据类型是8位、有符号的,以二进制补码表示的整数;
    最小值是 -128(-2^7);
    最大值是 127(2^7-1);
    默认值是 0;
    byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一;
    例子:byte a = 100,byte b = -50。
    short:
    short 数据类型是 16 位、有符号的以二进制补码表示的整数
    最小值是 -32768(-2^15);
    最大值是 32767(2^15 – 1);
    Short 数据类型也可以像 byte 那样节省空间。一个short变量是int型变量所占空间的二分之一;
    默认值是 0;
    例子:short s = 1000,short r = -20000。
    int:
    int 数据类型是32位、有符号的以二进制补码表示的整数;
    最小值是 -2,147,483,648(-2^31);
    最大值是 2,147,483,647(2^31 – 1);
    一般地整型变量默认为 int 类型;
    默认值是 0 ;
    例子:int a = 100000, int b = -200000。
    long:
    long 数据类型是 64 位、有符号的以二进制补码表示的整数;
    最小值是 -9,223,372,036,854,775,808(-2^63);
    最大值是 9,223,372,036,854,775,807(2^63 -1);
    这种类型主要使用在需要比较大整数的系统上;
    默认值是 0L;
    例子: long a = 100000L,Long b = -200000L。
    “L”理论上不分大小写,但是若写成”l”容易与数字”1″混淆,不容易分辩。所以最好大写。
    float:
    float 数据类型是单精度、32位、符合IEEE 754标准的浮点数;
    float 在储存大型浮点数组的时候可节省内存空间;
    默认值是 0.0f;
    浮点数不能用来表示精确的值,如货币;
    例子:float f1 = 234.5f。
    double:
    double 数据类型是双精度、64 位、符合IEEE 754标准的浮点数;
    浮点数的默认类型为double类型;
    double类型同样不能表示精确的值,如货币;
    默认值是 0.0d;
    例子:double d1 = 123.4。
    boolean:
    boolean数据类型表示一位的信息;
    只有两个取值:true 和 false;
    这种类型只作为一种标志来记录 true/false 情况;
    默认值是 false;
    例子:boolean one = true。
    char:
    char类型是一个单一的 16 位 Unicode 字符;
    最小值是 \u0000(即为0);
    最大值是 \uffff(即为65,535);
    char 数据类型可以储存任何字符;
    例子:char letter = ‘A’;。

    类型默认值
    byte 0
    short 0
    int 0
    long 0L
    float 0.0f
    double 0.0d
    char ‘u0000’
    String (or any object) null
    boolean false

    引用类型
    在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,比如 Employee、Puppy 等。变量一旦声明后,类型就不能被改变了。
    对象、数组都是引用数据类型。
    所有引用类型的默认值都是null。
    一个引用变量可以用来引用任何与之兼容的类型。
    例子:Site site = new Site(“Runoob”)。

    Java 常量
    常量在程序运行时是不能被修改的。
    在 Java 中使用 final 关键字来修饰常量,声明方式和变量类似:
    final double PI = 3.1415927;
    虽然常量名也可以用小写,但为了便于识别,通常使用大写字母表示常量。
    byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示。
    当使用常量的时候,前缀 0 表示 8 进制,而前缀 0x 代表 16 进制, 例如:
    int decimal = 100;
    int octal = 0144;
    int hexa = 0x64;
    Java的字符串常量是包含在两个引号之间的字符序列。
    “Hello World”
    “two\nlines”
    “\”This is in quotes\””
    字符串常量和字符常量都可以包含任何Unicode字符。例如:
    char a = ‘\u0001’;
    String a = “\u0001”;
    Java语言支持一些特殊的转义字符序列。
    \n 换行 (0x0a)
    \r 回车 (0x0d)
    \f 换页符(0x0c)
    \b 退格 (0x08)
    \0 空字符 (0x20)
    \s 字符串
    \t 制表符
    \” 双引号
    \’ 单引号
    \ 反斜杠
    \ddd 八进制字符 (ddd)
    \uxxxx 16进制Unicode字符 (xxxx)

    自动类型转换
    整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。
    转换从低级到高级。
    byte,short,char—> int —> long—> float —> double

    1. 不能对boolean类型进行类型转换。
    2. 不能把对象类型转换成不相关类的对象。
    3. 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
    4. 转换过程中可能导致溢出或损失精度,例如:
      int i =128;
      byte b = (byte)i;
      因为 byte 类型是 8 位,最大值为127,所以当 int 强制转换为 byte 类型时,值 128 时候就会导致溢出。
    5. 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如:
      (int)23.7 == 23;
      (int)-45.89f == -45
      必须满足转换前的数据类型的位数要低于转换后的数据类型,例如: short数据类型的位数为16位,就可以自动转换位数为32的int类型,同样float数据类型的位数为32,可以自动转换为64位的double类型。

    强制类型转换

    1. 条件是转换的数据类型必须是兼容的。
    2. 格式:(type)value type是要强制类型转换后的数据类型 实例:
    public class TypeConversion{
        public static void main(String[] args){
            int i1 = 123;
            byte b = (byte)i1;//强制类型转换为byte
            System.out.println("int强制类型转换为byte后的值等于"+b);
        }
    }

    Java 变量类型
    在Java语言中,所有的变量在使用前必须声明。声明变量的基本格式如下:
    type identifier [ = value][, identifier [= value] …] ;
    格式说明:type为Java数据类型。identifier是变量名。可以使用逗号隔开来声明多个同类型变量。
    以下列出了一些变量的声明实例。注意有些包含了初始化过程。
    int a, b, c; // 声明三个int型整数:a、 b、c
    int d = 3, e = 4, f = 5; // 声明三个整数并赋予初值
    byte z = 22; // 声明并初始化 z
    String s = “runoob”; // 声明并初始化字符串 s
    double pi = 3.14159; // 声明了双精度浮点型变量 pi
    char x = ‘x’; // 声明变量 x 的值是字符 ‘x’。

    Java语言支持的变量类型有:
    类变量:独立于方法之外的变量,用 static 修饰。
    实例变量:独立于方法之外的变量,不过没有 static 修饰。
    局部变量:类的方法中的变量。
    public class Variable{
    static int allClicks=0; // 类变量
    String str=”hello world”; // 实例变量
    public void method(){
    int i =0; // 局部变量
    }
    }

    Java 局部变量
    局部变量声明在方法、构造方法或者语句块中;
    局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁;
    访问修饰符不能用于局部变量;
    局部变量只在声明它的方法、构造方法或者语句块中可见;
    局部变量是在栈上分配的。
    局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。

    实例变量
    实例变量声明在一个类中,但在方法、构造方法和语句块之外;
    当一个对象被实例化之后,每个实例变量的值就跟着确定;
    实例变量在对象创建的时候创建,在对象被销毁的时候销毁;
    实例变量的值应该至少被一个方法、构造方法或者语句块引用,使得外部能够通过这些方式获取实例变量信息;
    实例变量可以声明在使用前或者使用后;
    访问修饰符可以修饰实例变量;
    实例变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把实例变量设为私有。通过使用访问修饰符可以使实例变量对子类可见;
    实例变量具有默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定;
    实例变量可以直接通过变量名访问。但在静态方法以及其他类中,就应该使用完全限定名:ObejectReference.VariableName。

    类变量(静态变量)
    类变量也称为静态变量,在类中以 static 关键字声明,但必须在方法之外。
    无论一个类创建了多少个对象,类只拥有类变量的一份拷贝。
    静态变量除了被声明为常量外很少使用。常量是指声明为public/private,final和static类型的变量。常量初始化后不可改变。
    静态变量储存在静态存储区。经常被声明为常量,很少单独使用static声明变量。
    静态变量在第一次被访问时创建,在程序结束时销毁。
    与实例变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为public类型。
    默认值和实例变量相似。数值型变量默认值是0,布尔型默认值是false,引用类型默认值是null。变量的值可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。
    静态变量可以通过:ClassName.VariableName的方式访问。
    类变量被声明为public static final类型时,类变量名称一般建议使用大写字母。如果静态变量不是public和final类型,其命名方式与实例变量以及局部变量的命名方式一致。

    import java.io.*;
    public class Employee {
        //salary是静态的私有变量
        private static double salary;
        // DEPARTMENT是一个常量
        public static final String DEPARTMENT = "开发人员";
        public static void main(String[] args){
        salary = 10000;
            System.out.println(DEPARTMENT+"平均工资:"+salary);
        }
    }

    Java 修饰符
    访问控制修饰符
    Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。
    default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
    private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
    public : 对所有类可见。使用对象:类、接口、变量、方法
    protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
    修饰符 当前类 同一包内 子孙类(同一包) 子孙类(不同包) 其他包
    public Y Y Y Y Y
    protected Y Y Y Y/N(说明) N
    default Y Y Y N N
    private Y N N N N

    默认访问修饰符-不使用任何关键字
    使用默认访问修饰符声明的变量和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为 public static final,而接口里的方法默认情况下访问权限为 public。

    私有访问修饰符-private
    私有访问修饰符是最严格的访问级别,所以被声明为 private 的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为 private。
    声明为私有访问类型的变量只能通过类中公共的 getter 方法被外部类访问。
    Private 访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据。

    公有访问修饰符-public
    被声明为 public 的类、方法、构造方法和接口能够被任何其他类访问。
    如果几个相互访问的 public 类分布在不同的包中,则需要导入相应 public 类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。

    受保护的访问修饰符-protected
    protected 需要从以下两个点来分析说明:
    子类与基类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问;
    子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问基类实例的protected方法。

    访问控制和继承
    请注意以下方法继承的规则:
    父类中声明为 public 的方法在子类中也必须为 public。
    父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
    父类中声明为 private 的方法,不能够被继承。

    非访问修饰符
    为了实现一些其他的功能,Java 也提供了许多非访问修饰符。
    static 修饰符,用来修饰类方法和类变量。
    final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
    abstract 修饰符,用来创建抽象类和抽象方法。
    synchronized 和 volatile 修饰符,主要用于线程的编程。

    static 修饰符
    静态变量:static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。
    静态方法:static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。
    对类变量和方法的访问可以直接使用 classname.variablename 和 classname.methodname 的方式访问。

    public class InstanceCounter {
       private static int numInstances = 0;
       protected static int getCount() {
          return numInstances;
       }
       private static void addInstance() {
          numInstances++;
       }
       InstanceCounter() {
          InstanceCounter.addInstance();
       }
       public static void main(String[] arguments) {
          System.out.println("Starting with " +
          InstanceCounter.getCount() + " instances");
          for (int i = 0; i < 500; ++i){
             new InstanceCounter();
              }
          System.out.println("Created " +
          InstanceCounter.getCount() + " instances");
       }
    }

    final 修饰符
    final 变量:
    final 表示”最后的、最终的”含义,变量一旦赋值后,不能被重新赋值。被 final 修饰的实例变量必须显式指定初始值。
    final 修饰符通常和 static 修饰符一起使用来创建类常量。
    final 方法:
    父类中的 final 方法可以被子类继承,但是不能被子类重写。声明 final 方法的主要目的是防止该方法的内容被修改。
    final 类:
    final 类不能被继承,没有类能够继承 final 类的任何特性。

    abstract 修饰符
    抽象类:
    抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
    一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。
    抽象类可以包含抽象方法和非抽象方法。
    抽象方法
    抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。
    抽象方法不能被声明成 final 和 static。
    任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
    如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。
    抽象方法的声明以分号结尾,例如:public abstract sample();。

    synchronized 修饰符
    synchronized 关键字声明的方法同一时间只能被一个线程访问。synchronized 修饰符可以应用于四个访问修饰符。

    transient 修饰符
    序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
    该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
    public transient int limit = 55; // 不会持久化
    public int b; // 持久化

    volatile 修饰符
    volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
    一个 volatile 对象引用可能是 null。

    Java 运算符
    算术运算符
    关系运算符
    位运算符
    逻辑运算符
    赋值运算符
    其他运算符

    算术运算符
    假设整数变量A的值为10,变量B的值为20

    • 加法 – 相加运算符两侧的值 A + B 等于 30
    • 减法 – 左操作数减去右操作数 A – B 等于 -10
    • 乘法 – 相乘操作符两侧的值 A * B等于200
      / 除法 – 左操作数除以右操作数 B / A等于2
      % 取余 – 左操作数除以右操作数的余数 B%A等于0
      ++ 自增: 操作数的值增加1 B++ 或 ++B 等于 21(区别详见下文)
      — 自减: 操作数的值减少1 B– 或 –B 等于 19(区别详见下文)
      自增自减运算符
      1、自增(++)自减(–)运算符是一种特殊的算术运算符,在算术运算符中需要两个操作数来进行运算,而自增自减运算符是一个操作数。
      2、前缀自增自减法(++a,–a): 先进行自增或者自减运算,再进行表达式运算。
      3、后缀自增自减法(a++,a–): 先进行表达式运算,再进行自增或者自减运算。

    关系运算符
    假设整数变量A的值为10,变量B的值为20
    == 检查如果两个操作数的值是否相等,如果相等则条件为真。 (A == B)为假。
    != 检查如果两个操作数的值是否相等,如果值不相等则条件为真。 (A != B) 为真。

    〉检查左操作数的值是否大于右操作数的值,如果是那么条件为真。   (A> B)为假。

    < 检查左操作数的值是否小于右操作数的值,如果是那么条件为真。 (A = B)为假。
    <= 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真。 (A <= B)为真。

    位运算符
    Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。
    位运算符作用在所有的位上,并且按位运算。假设a = 60,b = 13;它们的二进制格式表示将如下:
    A = 0011 1100

    B = 0000 1101

    A&B = 0000 1100
    A | B = 0011 1101
    A ^ B = 0011 0001
    ~A= 1100 0011
    假设整数变量 A 的值为 60 和变量 B 的值为 13
    & 如果相对应位都是1,则结果为1,否则为0 (A&B),得到12,即0000 1100
    | 如果相对应位都是 0,则结果为 0,否则为 1 (A | B)得到61,即 0011 1101
    ^ 如果相对应位值相同,则结果为0,否则为1 (A ^ B)得到49,即 0011 0001
    〜 按位取反运算符翻转操作数的每一位,即0变成1,1变成0。 (〜A)得到-61,即1100 0011
    << 按位左移运算符。左操作数按位左移右操作数指定的位数。 A << 2得到240,即 1111 0000

    按位右移运算符。左操作数按位右移右操作数指定的位数。  A >> 2得到15即 1111
    按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。   A>>>2得到15即0000 1111

    逻辑运算符
    假设布尔变量A为真,变量B为假
    && 称为逻辑与运算符。当且仅当两个操作数都为真,条件才为真。 (A && B)为假。
    | | 称为逻辑或操作符。如果任何两个操作数任何一个为真,条件为真。 (A | | B)为真。
    ! 称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。 !(A && B)为真。

    短路逻辑运算符
    当使用与逻辑运算符时,在两个操作数都为true时,结果才为true,但是当得到第一个操作为false时,其结果就必定是false,这时候就不会再判断第二个操作了。
    public class LuoJi{
    public static void main(String[] args){
    int a = 5;//定义一个变量;
    boolean b = (a<4)&&(a++<10);
    System.out.println(“使用短路逻辑运算符的结果为”+b);
    System.out.println(“a的结果为”+a);
    }
    }

    赋值运算符
    = 简单的赋值运算符,将右操作数的值赋给左侧操作数 C = A + B将把A + B得到的值赋给C
    += 加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数 C + = A等价于C = C + A
    -= 减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数 C – = A等价于C = C – A
    *= 乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数 C * = A等价于C = C * A
    /= 除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数 C / = A,C 与 A 同类型时等价于 C = C / A
    %= 取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数 C%= A等价于C = C%A
    <<= 左移位赋值运算符 C << = 2等价于C = C << 2

    = 右移位赋值运算符 C >> = 2等价于C = C >> 2
    &= 按位与赋值运算符 C&= 2等价于C = C&2
    ^= 按位异或赋值操作符 C ^ = 2等价于C = C ^ 2
    |= 按位或赋值操作符 C | = 2等价于C = C | 2

    条件运算符(?:)
    条件运算符也被称为三元运算符。该运算符有3个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。
    variable x = (expression) ? value if true : value if false

    instanceof运算符
    该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。
    instanceof运算符使用格式如下:
    ( Object reference variable ) instanceof (class/interface type)
    如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,那么结果为真。
    String name = “James”;
    boolean result = name instanceof String; // 由于 name 是 String 类型,所以返回真
    如果被比较的对象兼容于右侧类型,该运算符仍然返回true。
    class Vehicle {}
    public class Car extends Vehicle {
    public static void main(String[] args){
    Vehicle a = new Car();
    boolean result = a instanceof Car;
    System.out.println( result);
    }
    }

    Java运算符优先级
    下表中具有最高优先级的运算符在的表的最上面,最低优先级的在表的底部。
    类别 操作符 关联性
    后缀 () [] . (点操作符) 左到右
    一元 + + – !〜 从右到左
    乘性 * /% 左到右
    加性 + – 左到右
    移位 >> >>> << 左到右 关系 >> = << = 左到右 相等 == != 左到右 按位与 & 左到右 按位异或 ^ 左到右 按位或 | 左到右 逻辑与 && 左到右 逻辑或 | | 左到右 条件 ?: 从右到左 赋值 = + = – = * = / =%= >> = << =&= ^ = | = 从右到左
    逗号 , 左到右

    Java 循环结构 – for, while 及 do…while
    Java中有三种主要的循环结构:
    while 循环
    do…while 循环
    for 循环

    while 循环
    while是最基本的循环,它的结构为:
    while( 布尔表达式 ) {
    //循环内容
    }
    只要布尔表达式为 true,循环就会一直执行下去。

    do…while 循环
    对于 while 语句而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次。
    do…while 循环和 while 循环相似,不同的是,do…while 循环至少会执行一次。
    do {
    //代码语句
    }while(布尔表达式);

    for循环
    虽然所有循环结构都可以用 while 或者 do…while表示,但 Java 提供了另一种语句 —— for 循环,使一些循环结构变得更加简单。
    for循环执行的次数是在执行前就确定的。语法格式如下:
    for(初始化; 布尔表达式; 更新) {
    //代码语句
    }
    最先执行初始化步骤。可以声明一种类型,但可初始化一个或多个循环控制变量,也可以是空语句。
    然后,检测布尔表达式的值。如果为 true,循环体被执行。如果为false,循环终止,开始执行循环体后面的语句。
    执行一次循环后,更新循环控制变量。
    再次检测布尔表达式。循环执行上面的过程。

    Java 增强 for 循环
    Java5 引入了一种主要用于数组的增强型 for 循环。
    Java 增强 for 循环语法格式如下:
    for(声明语句 : 表达式)
    {
    //代码句子
    }
    声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
    表达式:表达式是要访问的数组名,或者是返回值为数组的方法。

    public class Test {
       public static void main(String args[]){
          int [] numbers = {10, 20, 30, 40, 50};
    
          for(int x : numbers ){
             System.out.print( x );
             System.out.print(",");
          }
          System.out.print("\n");
          String [] names ={"James", "Larry", "Tom", "Lacy"};
          for( String name : names ) {
             System.out.print( name );
             System.out.print(",");
          }
       }
    }

    break 关键字
    break 主要用在循环语句或者 switch 语句中,用来跳出整个语句块。
    break 跳出最里层的循环,并且继续执行该循环下面的语句。

    continue 关键字
    continue 适用于任何循环控制结构中。作用是让程序立刻跳转到下一次循环的迭代。
    在 for 循环中,continue 语句使程序立即跳转到更新语句。
    在 while 或者 do…while 循环中,程序立即跳转到布尔表达式的判断语句。

    Java 条件语句 – if…else
    一个 if 语句包含一个布尔表达式和一条或多条语句。
    语法
    if 语句的语法如下:
    if(布尔表达式)
    {
    //如果布尔表达式为true将执行的语句
    }
    如果布尔表达式的值为 true,则执行 if 语句中的代码块,否则执行 if 语句块后面的代码。

    if…else语句
    if 语句后面可以跟 else 语句,当 if 语句的布尔表达式值为 false 时,else 语句块会被执行。
    语法
    if…else 的用法如下:
    if(布尔表达式){
    //如果布尔表达式的值为true
    }else{
    //如果布尔表达式的值为false
    }

    if…else if…else 语句
    if 语句后面可以跟 else if…else 语句,这种语句可以检测到多种可能的情况。
    使用 if,else if,else 语句的时候,需要注意下面几点:
    if 语句至多有 1 个 else 语句,else 语句在所有的 else if 语句之后。
    if 语句可以有若干个 else if 语句,它们必须在 else 语句之前。
    一旦其中一个 else if 语句检测为 true,其他的 else if 以及 else 语句都将跳过执行。
    语法
    if…else 语法格式如下:
    if(布尔表达式 1){
    //如果布尔表达式 1的值为true执行代码
    }else if(布尔表达式 2){
    //如果布尔表达式 2的值为true执行代码
    }else if(布尔表达式 3){
    //如果布尔表达式 3的值为true执行代码
    }else {
    //如果以上布尔表达式都不为true执行代码
    }

    嵌套的 if…else 语句
    使用嵌套的 if…else 语句是合法的。也就是说你可以在另一个 if 或者 else if 语句中使用 if 或者 else if 语句。
    语法
    嵌套的 if…else 语法格式如下:
    if(布尔表达式 1){
    ////如果布尔表达式 1的值为true执行代码
    if(布尔表达式 2){
    ////如果布尔表达式 2的值为true执行代码
    }
    }

    Java switch case 语句
    switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
    语法
    switch case 语句语法格式如下:
    switch(expression){
    case value :
    //语句
    break; //可选
    case value :
    //语句
    break; //可选
    //你可以有任意数量的case语句
    default : //可选
    //语句
    }
    switch case 语句有如下规则:
    switch 语句中的变量类型可以是: byte、short、int 或者 char。从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量。
    switch 语句可以拥有多个 case 语句。每个 case 后面跟一个要比较的值和冒号。
    case 语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面常量。
    当变量的值与 case 语句的值相等时,那么 case 语句之后的语句开始执行,直到 break 语句出现才会跳出 switch 语句。
    当遇到 break 语句时,switch 语句终止。程序跳转到 switch 语句后面的语句执行。case 语句不必须要包含 break 语句。如果没有 break 语句出现,程序会继续执行下一条 case 语句,直到出现 break 语句。
    switch 语句可以包含一个 default 分支,该分支一般是 switch 语句的最后一个分支(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。
    switch case 执行时,一定会先进行匹配,匹配成功返回当前 case 的值,再根据是否有 break,判断是否继续输出,或是跳出判断。
    如果 case 语句块中没有 break 语句时,JVM 并不会顺序输出每一个 case 对应的返回值,而是继续匹配,匹配不成功则返回默认 case。
    如果当前匹配成功的 case 语句块没有 break 语句,则从当前 case 开始,后续所有 case 的值都会输出,如果后续的 case 语句块有 break 语句则会跳出判断。

    Java Number & Math 类
    Java 语言为每一个内置数据类型提供了对应的包装类。
    所有的包装类(Integer、Long、Byte、Double、Float、Short)都是抽象类 Number 的子类。
    基本类型 包装类
    double Double
    float Float
    long Long
    int Integer
    short Short
    byte Byte
    char Character
    boolean Boolean

    Java Math 类
    Java 的 Math 包含了用于执行基本数学运算的属性和方法,如初等指数、对数、平方根和三角函数。
    Math 的方法都被定义为 static 形式,通过 Math 类可以在主函数中直接调用。

    public class Test {
        public static void main (String []args)
        {
            System.out.println("90 度的正弦值:" + Math.sin(Math.PI/2));
            System.out.println("0度的余弦值:" + Math.cos(0));
            System.out.println("60度的正切值:" + Math.tan(Math.PI/3));
            System.out.println("1的反正切值: " + Math.atan(1));
            System.out.println("π/2的角度值:" + Math.toDegrees(Math.PI/2));
            System.out.println(Math.PI);
        }
    }

    Number & Math 类方法
    1 xxxValue()
    将 Number 对象转换为xxx数据类型的值并返回。
    2 compareTo()
    将number对象与参数比较。
    3 equals()
    判断number对象是否与参数相等。
    4 valueOf()
    返回一个 Number 对象指定的内置数据类型
    5 toString()
    以字符串形式返回值。
    6 parseInt()
    将字符串解析为int类型。
    7 abs()
    返回参数的绝对值。
    8 ceil()
    返回大于等于( >= )给定参数的的最小整数,类型为双精度浮点型。
    9 floor()
    返回小于等于(<=)给定参数的最大整数 。
    10 rint()
    返回与参数最接近的整数。返回类型为double。
    11 round()
    它表示四舍五入,算法为 Math.floor(x+0.5),即将原来的数字加上 0.5 后再向下取整,所以,Math.round(11.5) 的结果为12,Math.round(-11.5) 的结果为-11。
    12 min()
    返回两个参数中的最小值。
    13 max()
    返回两个参数中的最大值。
    14 exp()
    返回自然数底数e的参数次方。
    15 log()
    返回参数的自然数底数的对数值。
    16 pow()
    返回第一个参数的第二个参数次方。
    17 sqrt()
    求参数的算术平方根。
    18 sin()
    求指定double类型参数的正弦值。
    19 cos()
    求指定double类型参数的余弦值。
    20 tan()
    求指定double类型参数的正切值。
    21 asin()
    求指定double类型参数的反正弦值。
    22 acos()
    求指定double类型参数的反余弦值。
    23 atan()
    求指定double类型参数的反正切值。
    24 atan2()
    将笛卡尔坐标转换为极坐标,并返回极坐标的角度值。
    25 toDegrees()
    将参数转化为角度。
    26 toRadians()
    将角度转换为弧度。
    27 random()
    返回一个随机数。

    Java Character 类
    Java语言为内置数据类型char提供了包装类Character类。
    Character ch = new Character(‘a’);
    下面是Character类的方法:
    1 isLetter()是否是一个字母
    2 isDigit()是否是一个数字字符
    3 isWhitespace()是否是一个空白字符
    4 isUpperCase()是否是大写字母
    5 isLowerCase()是否是小写字母
    6 toUpperCase()指定字母的大写形式
    7 toLowerCase()指定字母的小写形式
    8 toString()返回字符的字符串形式,字符串的长度仅为1

    Java String 类
    Java 提供了 String 类来创建和操作字符串。
    String greeting = “菜鸟教程”;
    String 类有 11 种构造方法,这些方法提供不同的参数来初始化字符串,比如提供一个字符数组参数:

    public class StringDemo{
       public static void main(String args[]){
          char[] helloArray = { 'r', 'u', 'n', 'o', 'o', 'b'};
          String helloString = new String(helloArray);
          System.out.println( helloString );
       }
    }

    字符串长度
    String 类的一个访问器方法是 length() 方法,它返回字符串对象包含的字符数。

    public class StringDemo {
        public static void main(String args[]) {
            String site = "www.runoob.com";
            int len = site.length();
            System.out.println( "菜鸟教程网址长度 : " + len );
       }
    }

    连接字符串
    String 类提供了连接两个字符串的方法:
    string1.concat(string2);
    返回 string2 连接 string1 的新字符串。也可以对字符串常量使用 concat() 方法,如

    public class StringDemo {
        public static void main(String args[]) {
            String string1 = "菜鸟教程网址:";
            System.out.println("1、" + string1 + "www.runoob.com");
        }
    }

    创建格式化字符串
    我们知道输出格式化数字可以使用 printf() 和 format() 方法。
    String 类使用静态方法 format() 返回一个String 对象而不是 PrintStream 对象。
    String 类的静态方法 format() 能用来创建可复用的格式化字符串,而不仅仅是用于一次打印输出。

    String fs;
    fs = String.format("浮点型变量的值为 " +
                       "%f, 整型变量的值为 " +
                       " %d, 字符串变量的值为 " +
                       " %s", floatVar, intVar, stringVar);

    String 方法
    1 char charAt(int index) 返回指定索引处的 char 值。
    2 int compareTo(Object o) 把这个字符串和另一个对象比较。
    3 int compareTo(String anotherString) 按字典顺序比较两个字符串。
    4 int compareToIgnoreCase(String str) 按字典顺序比较两个字符串,不考虑大小写。
    5 String concat(String str) 将指定字符串连接到此字符串的结尾。
    6 boolean contentEquals(StringBuffer sb) 当且仅当字符串与指定的StringBuffer有相同顺序的字符时候返回真。
    7 static String copyValueOf(char[] data) 返回指定数组中表示该字符序列的 String。
    8 static String copyValueOf(char[] data, int offset, int count) 返回指定数组中表示该字符序列的 String。
    9 boolean endsWith(String suffix) 测试此字符串是否以指定的后缀结束。
    10 boolean equals(Object anObject) 将此字符串与指定的对象比较。
    11 boolean equalsIgnoreCase(String anotherString) 将此 String 与另一个 String 比较,不考虑大小写。
    12 byte[] getBytes() 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
    13 byte[] getBytes(String charsetName) 使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
    14 void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 将字符从此字符串复制到目标字符数组。
    15 int hashCode() 返回此字符串的哈希码。
    16 int indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引。
    17 int indexOf(int ch, int fromIndex) 返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。
    18 int indexOf(String str) 返回指定子字符串在此字符串中第一次出现处的索引。
    19 int indexOf(String str, int fromIndex) 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。
    20 String intern() 返回字符串对象的规范化表示形式。
    21 int lastIndexOf(int ch) 返回指定字符在此字符串中最后一次出现处的索引。
    22 int lastIndexOf(int ch, int fromIndex) 返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索。
    23 int lastIndexOf(String str) 返回指定子字符串在此字符串中最右边出现处的索引。
    24 int lastIndexOf(String str, int fromIndex) 返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。
    25 int length() 返回此字符串的长度。
    26 boolean matches(String regex) 告知此字符串是否匹配给定的正则表达式。
    27 boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) 测试两个字符串区域是否相等。
    28 boolean regionMatches(int toffset, String other, int ooffset, int len) 测试两个字符串区域是否相等。
    29 String replace(char oldChar, char newChar) 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
    30 String replaceAll(String regex, String replacement) 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
    31 String replaceFirst(String regex, String replacement) 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
    32 String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串。
    33 String[] split(String regex, int limit) 根据匹配给定的正则表达式来拆分此字符串。
    34 boolean startsWith(String prefix) 测试此字符串是否以指定的前缀开始。
    35 boolean startsWith(String prefix, int toffset) 测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
    36 CharSequence subSequence(int beginIndex, int endIndex) 返回一个新的字符序列,它是此序列的一个子序列。
    37 String substring(int beginIndex) 返回一个新的字符串,它是此字符串的一个子字符串。
    38 String substring(int beginIndex, int endIndex) 返回一个新字符串,它是此字符串的一个子字符串。
    39 char[] toCharArray() 将此字符串转换为一个新的字符数组。
    40 String toLowerCase() 使用默认语言环境的规则将此 String 中的所有字符都转换为小写。
    41 String toLowerCase(Locale locale) 使用给定 Locale 的规则将此 String 中的所有字符都转换为小写。
    42 String toString() 返回此对象本身(它已经是一个字符串!)。
    43 String toUpperCase() 使用默认语言环境的规则将此 String 中的所有字符都转换为大写。
    44 String toUpperCase(Locale locale) 使用给定 Locale 的规则将此 String 中的所有字符都转换为大写。
    45 String trim() 返回字符串的副本,忽略前导空白和尾部空白。
    46 static String valueOf(primitive data type x) 返回给定data type类型x参数的字符串表示形式。

    Java StringBuffer 和 StringBuilder 类
    当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
    由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

    public class Test{
      public static void main(String args[]){
        StringBuffer sBuffer = new StringBuffer("菜鸟教程官网:");
        sBuffer.append("www");
        sBuffer.append(".runoob");
        sBuffer.append(".com");
        System.out.println(sBuffer);
      }
    }

    StringBuffer 方法
    1 public StringBuffer append(String s) 将指定的字符串追加到此字符序列。
    2 public StringBuffer reverse() 将此字符序列用其反转形式取代。
    3 public delete(int start, int end) 移除此序列的子字符串中的字符。
    4 public insert(int offset, int i) 将 int 参数的字符串表示形式插入此序列中。
    5 replace(int start, int end, String str) 使用给定 String 中的字符替换此序列的子字符串中的字符。
    下面的列表里的方法和 String 类的方法类似:
    1 int capacity() 返回当前容量。
    2 char charAt(int index) 返回此序列中指定索引处的 char 值。
    3 void ensureCapacity(int minimumCapacity) 确保容量至少等于指定的最小值。
    4 void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 将字符从此序列复制到目标字符数组 dst。
    5 int indexOf(String str) 返回第一次出现的指定子字符串在该字符串中的索引。
    6 int indexOf(String str, int fromIndex) 从指定的索引处开始,返回第一次出现的指定子字符串在该字符串中的索引。
    7 int lastIndexOf(String str) 返回最右边出现的指定子字符串在此字符串中的索引。
    8 int lastIndexOf(String str, int fromIndex) 返回 String 对象中子字符串最后出现的位置。
    9 int length() 返回长度(字符数)。
    10 void setCharAt(int index, char ch) 将给定索引处的字符设置为 ch。
    11 void setLength(int newLength) 设置字符序列的长度。
    12 CharSequence subSequence(int start, int end) 返回一个新的字符序列,该字符序列是此序列的子序列。
    13 String substring(int start) 返回一个新的 String,它包含此字符序列当前所包含的字符子序列。
    14 String substring(int start, int end) 返回一个新的 String,它包含此序列当前所包含的字符子序列。
    15 String toString() 返回此序列中数据的字符串表示形式。

    Java 数组
    Java 语言中提供的数组是用来存储固定大小的同类型元素。
    声明数组变量
    首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:
    dataType[] arrayRefVar; // 首选的方法

    dataType arrayRefVar[]; // 效果相同,但不是首选方法

    创建数组
    Java语言使用new操作符来创建数组,语法如下:
    arrayRefVar = new dataType[arraySize];
    上面的语法语句做了两件事:
    一、使用 dataType[arraySize] 创建了一个数组。
    二、把新创建的数组的引用赋值给变量 arrayRefVar。
    数组变量的声明,和创建数组可以用一条语句完成,如下所示:
    dataType[] arrayRefVar = new dataType[arraySize];
    另外,你还可以使用如下的方式创建数组。
    dataType[] arrayRefVar = {value0, value1, …, valuek};
    数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 arrayRefVar.length-1。

    处理数组
    数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本循环或者 For-Each 循环。

    public class TestArray {
       public static void main(String[] args) {
          double[] myList = {1.9, 2.9, 3.4, 3.5};
          // 打印所有数组元素
          for (int i = 0; i < myList.length; i++) {
             System.out.println(myList[i] + " ");
          }
          // 计算所有元素的总和
          double total = 0;
          for (int i = 0; i < myList.length; i++) {
             total += myList[i];
          }
          System.out.println("Total is " + total);
          // 查找最大元素
          double max = myList[0];
          for (int i = 1; i < myList.length; i++) {
             if (myList[i] > max) max = myList[i];
          }
          System.out.println("Max is " + max);
          // 打印所有数组元素
          for (double element: myList) {
             System.out.println(element);
          }
       }
    }

    数组作为函数的参数
    数组可以作为参数传递给方法。

    public static void printArray(int[] array) {
      for (int i = 0; i < array.length; i++) {
        System.out.print(array[i] + " ");
      }
    }
    printArray(new int[]{3, 1, 2, 6, 4, 2});

    数组作为函数的返回值

    public static int[] reverse(int[] list) {
      int[] result = new int[list.length];
    
      for (int i = 0, j = result.length - 1; i < list.length; i++, j--) {
        result[j] = list[i];
      }
      return result;
    }

    多维数组
    多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组,例如:
    String str[][] = new String[3][4];
    多维数组的动态初始化(以二维数组为例)

    1. 直接为每一维分配空间,格式如下:
      type[][] typeName = new type[typeLength1][typeLength2];
      type 可以为基本数据类型和复合数据类型,arraylength1 和 arraylength2 必须为正整数,arraylength1 为行数,arraylength2 为列数。例如:
      int a[][] = new int[2][3]; //二维数组 a 可以看成一个两行三列的数组。
    2. 从最高维开始,分别为每一维分配空间,例如:
      String s[][] = new String[2][];
      s[0] = new String[2];
      s[1] = new String[3];
      s[0][0] = new String(“Good”);
      s[0][1] = new String(“Luck”);
      s[1][0] = new String(“to”);
      s[1][1] = new String(“you”);
      s[1][2] = new String(“!”);
      解析:
      s[0]=new String[2] 和 s[1]=new String[3] 是为最高维分配引用空间,也就是为最高维限制其能保存数据的最长的长度,然后再为其每个数组元素单独分配空间 s0=new String(“Good”) 等操作。

    Arrays 类
    java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。
    具有以下功能:
    给数组赋值:通过 fill 方法。
    对数组排序:通过 sort 方法,按升序。
    比较数组:通过 equals 方法比较数组中元素值是否相等。
    查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
    1 public static int binarySearch(Object[] a, Object key)
    用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double等)。数组在调用前必须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) – 1)。
    2 public static boolean equals(long[] a, long[] a2)
    如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素,并且两个数组中的所有相应元素对都是相等的,则认为这两个数组是相等的。换句话说,如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
    3 public static void fill(int[] a, int val)
    将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。
    4 public static void sort(Object[] a)
    对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型(Byte,short,Int等)。

    Java 方法
    Java方法是语句的集合,它们在一起执行一个功能。
    方法是解决一类问题的步骤的有序组合
    方法包含于类或对象中
    方法在程序中被创建,在其他地方被引用
    方法的命名规则
    1.方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。
    2.下划线可能出现在 JUnit 测试方法名称中用以分隔名称的逻辑组件。一个典型的模式是:test_,例如 testPop_emptyStack。

    方法的定义
    一般情况下,定义一个方法包含以下语法:
    修饰符 返回值类型 方法名(参数类型 参数名){

    方法体

    return 返回值;
    }
    方法包含一个方法头和一个方法体。下面是一个方法的所有部分:
    修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
    返回值类型 :方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。
    方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
    参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
    方法体:方法体包含具体的语句,定义该方法的功能。

    /** 返回两个整型变量数据的较大值 */
    public static int max(int num1, int num2) {
       int result;
       if (num1 > num2)
          result = num1;
       else
          result = num2;
       return result;
    }

    方法调用
    Java 支持两种调用方法的方式,根据方法是否返回值来选择。
    当程序调用一个方法时,程序的控制权交给了被调用的方法。当被调用方法的返回语句执行或者到达方法体闭括号时候交还控制权给程序。
    当方法返回一个值的时候,方法调用通常被当做一个值。例如:
    int larger = max(30, 40);
    如果方法返回值是void,方法调用一定是一条语句。例如,方法println返回void。下面的调用是个语句:
    System.out.println(“欢迎访问菜鸟教程!”);

    通过值传递参数
    调用一个方法时候需要提供参数,你必须按照参数列表指定的顺序提供。

    方法的重载
    上面使用的max方法仅仅适用于int型数据。但如果你想得到两个浮点类型数据的最大值呢?
    解决方法是创建另一个有相同名字但参数不同的方法,如下面代码所示:

    public static double max(double num1, double num2) {
      if (num1 > num2)
        return num1;
      else
        return num2;
    }

    Java编译器根据方法签名判断哪个方法应该被调用。
    方法重载可以让程序更清晰易读。执行密切相关任务的方法应该使用相同的名字。
    重载的方法必须拥有不同的参数列表。你不能仅仅依据修饰符或者返回类型的不同来重载方法。

    命令行参数的使用
    有时候你希望运行一个程序时候再传递给它消息。这要靠传递命令行参数给main()函数实现。
    命令行参数是在执行程序时候紧跟在程序名字后面的信息。
    CommandLine.java 文件代码:

    public class CommandLine {
       public static void main(String args[]){
          for(int i=0; i<args.length; i++){
             System.out.println("args[" + i + "]: " + args[i]);
          }
       }
    }

    可变参数
    Java支持传递同类型的可变参数给一个方法。
    方法的可变参数的声明如下所示:
    typeName… parameterName
    在方法声明中,在指定参数类型后加一个省略号(…) 。
    一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。

    finalize() 方法
    Java 允许定义这样的方法,它在对象被垃圾收集器析构(回收)之前调用,这个方法叫做 finalize( ),它用来清除回收对象。
    例如,你可以使用 finalize() 来确保一个对象打开的文件被关闭了。
    在 finalize() 方法里,你必须指定在对象销毁时候要执行的操作。
    finalize() 一般格式是:
    protected void finalize()
    {
    // 在这里终结代码
    }
    关键字 protected 是一个限定符,它确保 finalize() 方法不会被该类以外的代码调用。
    当然,Java 的内存回收可以由 JVM 来自动完成。如果你手动使用,则可以使用上面的方法。

    public class FinalizationDemo {
      public static void main(String[] args) {
        Cake c1 = new Cake(1);
        Cake c2 = new Cake(2);
        Cake c3 = new Cake(3);
        c2 = c3 = null;
        System.gc(); //调用Java垃圾收集器
      }
    }
    class Cake extends Object {
      private int id;
      public Cake(int id) {
        this.id = id;
        System.out.println("Cake Object " + id + " is created");
      }
      protected void finalize() throws java.lang.Throwable {
        super.finalize();
        System.out.println("Cake Object " + id + " is disposed");
      }
    }

    Java 异常处理
    异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
    三种类型的异常:
    检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
    运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
    错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。

    Exception 类的层次
    所有的异常类是从 java.lang.Exception 类继承的子类。
    Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。
    异常类有两个主要的子类:IOException 类和 RuntimeException 类。
    Java 内置异常类
    Java 语言定义了一些异常类在 java.lang 标准包中。
    标准运行时异常类的子类是最常见的异常类。由于 java.lang 包是默认加载到所有的 Java 程序的,所以大部分从运行时异常类继承而来的异常都可以直接使用。
    Java 根据各个类库也定义了一些其他的异常,下面的表中列出了 Java 的非检查性异常。
    ArithmeticException 当出现异常的运算条件时,抛出此异常。例如,一个整数”除以零”时,抛出此类的一个实例。
    ArrayIndexOutOfBoundsException 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
    ArrayStoreException 试图将错误类型的对象存储到一个对象数组时抛出的异常。
    ClassCastException 当试图将对象强制转换为不是实例的子类时,抛出该异常。
    IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
    IllegalMonitorStateException 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。
    IllegalStateException 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。
    IllegalThreadStateException 线程没有处于请求操作所要求的适当状态时抛出的异常。
    IndexOutOfBoundsException 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
    NegativeArraySizeException 如果应用程序试图创建大小为负的数组,则抛出该异常。
    NullPointerException 当应用程序试图在需要对象的地方使用 null 时,抛出该异常
    NumberFormatException 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
    SecurityException 由安全管理器抛出的异常,指示存在安全侵犯。
    StringIndexOutOfBoundsException 此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。
    UnsupportedOperationException 当不支持请求的操作时,抛出该异常。
    下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类。
    ClassNotFoundException 应用程序试图加载类时,找不到相应的类,抛出该异常。
    CloneNotSupportedException 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。
    IllegalAccessException 拒绝访问一个类的时候,抛出该异常。
    InstantiationException 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。
    InterruptedException 一个线程被另一个线程中断,抛出该异常。
    NoSuchFieldException 请求的变量不存在
    NoSuchMethodException 请求的方法不存在

    异常方法
    下面的列表是 Throwable 类的主要方法:
    1 public String getMessage()
    返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。
    2 public Throwable getCause()
    返回一个Throwable 对象代表异常原因。
    3 public String toString()
    使用getMessage()的结果返回类的串级名字。
    4 public void printStackTrace()
    打印toString()结果和栈层次到System.err,即错误输出流。
    5 public StackTraceElement [] getStackTrace()
    返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。
    6 public Throwable fillInStackTrace()
    用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。

    捕获异常
    使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
    try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:
    try
    {
    // 程序代码
    }catch(ExceptionName e1)
    {
    //Catch 块
    }
    Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
    如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。

    多重捕获块
    一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获。
    多重捕获块的语法如下所示:
    try{
    // 程序代码
    }catch(异常类型1 异常的变量名1){
    // 程序代码
    }catch(异常类型2 异常的变量名2){
    // 程序代码
    }catch(异常类型2 异常的变量名2){
    // 程序代码
    }
    可以在 try 语句后面添加任意数量的 catch 块。
    如果保护代码中发生异常,异常被抛给第一个 catch 块。
    如果抛出异常的数据类型与 ExceptionType1 匹配,它在这里就会被捕获。
    如果不匹配,它会被传递给第二个 catch 块。
    如此,直到异常被捕获或者通过所有的 catch 块。

    throws/throw 关键字:
    如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。
    也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
    下面方法的声明抛出一个 RemoteException 异常:

    import java.io.*;
    public class className
    {
      public void deposit(double amount) throws RemoteException
      {
        // Method implementation
        throw new RemoteException();
      }
      //Remainder of class definition
    }

    一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。

    finally关键字
    finally 关键字用来创建在 try 代码块后面执行的代码块。
    无论是否发生异常,finally 代码块中的代码总会被执行。
    在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
    finally 代码块出现在 catch 代码块最后,语法如下:
    try{
    // 程序代码
    }catch(异常类型1 异常的变量名1){
    // 程序代码
    }catch(异常类型2 异常的变量名2){
    // 程序代码
    }finally{
    // 程序代码
    }

    public class ExcepTest{
      public static void main(String args[]){
        int a[] = new int[2];
        try{
           System.out.println("Access element three :" + a[3]);
        }catch(ArrayIndexOutOfBoundsException e){
           System.out.println("Exception thrown  :" + e);
        }
        finally{
           a[0] = 6;
           System.out.println("First element value: " +a[0]);
           System.out.println("The finally statement is executed");
        }
      }
    }

    catch 不能独立于 try 存在。
    在 try/catch 后面添加 finally 块并非强制性要求的。
    try 代码后不能既没 catch 块也没 finally 块。
    try, catch, finally 块之间不能添加任何代码。

    声明自定义异常
    在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
    所有异常都必须是 Throwable 的子类。
    如果希望写一个检查性异常类,则需要继承 Exception 类。
    如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
    只继承Exception 类来创建的异常类是检查性异常类。

    通用异常
    在Java中定义了两种类型的异常和错误。
    JVM(Java虚拟机) 异常:由 JVM 抛出的异常或错误。例如:NullPointerException 类,ArrayIndexOutOfBoundsException 类,ClassCastException 类。
    程序级异常:由程序或者API程序抛出的异常。例如 IllegalArgumentException 类,IllegalStateException 类。

    Java 继承
    继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
    继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
    类的继承格式
    在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:
    类的继承格式
    class 父类 {
    }
    class 子类 extends 父类 {
    }
    Java 不支持多继承,但支持多重继承。

    继承的特性
    子类拥有父类非 private 的属性、方法。
    子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
    子类可以用自己的方式实现父类的方法。
    Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
    提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。

    继承关键字
    继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。
    extends关键字
    在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。
    implements关键字
    使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。

    public interface A {
        public void eat();
        public void sleep();
    }
    public interface B {
        public void show();
    }
    public class C implements A,B {
    }

    super 与 this 关键字
    super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
    this关键字:指向自己的引用。

    final关键字
    final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写:
    声明类:
    final class 类名 {//类体}
    声明方法:
    修饰符(public/private/default/protected) final 返回值类型 方法名(){//方法体}

    构造器
    子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
    如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。

    Java 重写(Override)与重载(Overload)
    重写(Override)
    重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
    重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
    重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。
    在面向对象原则里,重写意味着可以重写任何现有方法。

    方法的重写规则
    参数列表必须完全与被重写方法的相同。
    返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
    访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
    父类的成员方法只能被它的子类重写。
    声明为 final 的方法不能被重写。
    声明为 static 的方法不能被重写,但是能够被再次声明。
    子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
    子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
    重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
    构造方法不能被重写。
    如果不能继承一个方法,则不能重写这个方法。

    重载(Overload)
    重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
    每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
    最常用的地方就是构造器的重载。
    重载规则:
    被重载的方法必须改变参数列表(参数个数或类型不一样);
    被重载的方法可以改变返回类型;
    被重载的方法可以改变访问修饰符;
    被重载的方法可以声明新的或更广的检查异常;
    方法能够在同一个类中或者在一个子类中被重载。
    无法以返回值类型作为重载函数的区分标准。

    重写与重载之间的区别
    区别点 重载方法 重写方法
    参数列表 必须修改 一定不能修改
    返回类型 可以修改 一定不能修改
    异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
    访问 可以修改 一定不能做更严格的限制(可以降低限制)
    方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
    (1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
    (2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
    (3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

    Java 多态
    多态是同一个行为具有多个不同表现形式或形态的能力。
    多态就是同一个接口,使用不同的实例而执行不同操作。
    多态存在的三个必要条件: 继承,重写,父类引用指向子类对象
    比如:
    Parent p = new Child();
    当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
    多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。

    public class Test {
        public static void main(String[] args) {
            show(new Cat());  // 以 Cat 对象调用 show 方法
            show(new Dog());  // 以 Dog 对象调用 show 方法
            Animal a = new Cat();  // 向上转型
            a.eat();               // 调用的是 Cat 的 eat
            Cat c = (Cat)a;        // 向下转型
            c.work();        // 调用的是 Cat 的 work
        }
        public static void show(Animal a)  {
            a.eat();
            // 类型判断
            if (a instanceof Cat)  {  // 猫做的事情
                Cat c = (Cat)a;
                c.work();
            } else if (a instanceof Dog) { // 狗做的事情
                Dog c = (Dog)a;
                c.work();
            }
        }
    }
    abstract class Animal {
        abstract void eat();
    }
    class Cat extends Animal {
        public void eat() {
            System.out.println("吃鱼");
        }
        public void work() {
            System.out.println("抓老鼠");
        }
    }
    class Dog extends Animal {
        public void eat() {
            System.out.println("吃骨头");
        }
        public void work() {
            System.out.println("看家");
        }
    }

    虚函数
    虚函数的存在是为了多态。
    Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。

    Java 抽象类
    在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
    抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
    由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
    父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
    在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。

    抽象方法
    如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。
    Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。
    抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。
    声明抽象方法会造成以下两个结果:
    如果一个类包含抽象方法,那么该类必须是抽象类。
    任何子类必须重写父类的抽象方法,或者声明自身为抽象类。

    Java 封装
    在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法。
    封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
    要访问该类的代码和数据,必须通过严格的接口控制。
    封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
    适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
    封装的优点

    1. 良好的封装能够减少耦合。
    2. 类内部的结构可以自由修改。
    3. 可以对成员变量进行更精确的控制。
    4. 隐藏信息,实现细节。

    实现Java封装的步骤

    1. 修改属性的可见性来限制对属性的访问(一般限制为private),例如:
      public class Person {
      private String name;
      private int age;
      }
      这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
    2. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:
      public class Person{
      private String name;
      private int age;
      public int getAge(){
      return age;
      }
      public String getName(){
      return name;
      }
      public void setAge(int age){
      this.age = age;
      }
      public void setName(String name){
      this.name = name;
      }
      }
      采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。

    Java 接口
    接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
    接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
    除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
    接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。

    接口与类相似点:
    一个接口可以有多个方法。
    接口文件保存在 .java 结尾的文件中,文件名使用接口名。
    接口的字节码文件保存在 .class 结尾的文件中。
    接口相应的字节码文件必须在与包名称相匹配的目录结构中。
    接口与类的区别:
    接口不能用于实例化对象。
    接口没有构造方法。
    接口中所有的方法必须是抽象方法。
    接口不能包含成员变量,除了 static 和 final 变量。
    接口不是被类继承了,而是要被类实现。
    接口支持多继承。
    抽象类和接口的区别

    1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
    2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
    3. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

    接口特性
    接口中的方法都是公有的。
    接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
    接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
    接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
    接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。

    接口的声明
    接口的声明语法格式如下:
    [可见度] interface 接口名称 [extends 其他的接口名] {
    // 声明变量
    // 抽象方法
    }
    Interface关键字用来声明一个接口。下面是接口声明的一个简单例子。

    接口的实现
    当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
    类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。
    实现一个接口的语法,可以使用这个公式:
    …implements 接口名称[, 其他接口名称, 其他接口名称…, …] …
    重写接口中声明的方法时,需要注意以下规则:
    类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
    类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型。
    如果实现接口的类是抽象类,那么就没必要实现该接口的方法。
    在实现接口的时候,也要注意一些规则:
    一个类可以同时实现多个接口。
    一个类只能继承一个类,但是能实现多个接口。
    一个接口能继承另一个接口,这和类之间的继承比较相似。

    接口的继承
    一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。
    接口的多继承
    在Java中,类的多继承是不合法,但接口允许多继承。
    在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。 如下所示:
    public interface Hockey extends Sports, Event
    以上的程序片段是合法定义的子接口,与类不同的是,接口允许多继承,而 Sports及 Event 可能定义或是继承相同的方法

    标记接口
    最常用的继承接口是没有包含任何方法的接口。
    标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。
    标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
    标记接口主要用于以下两种目的:
    建立一个公共的父接口:
    正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
    向一个类添加数据类型:
    这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。

    Java 包(package)
    为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。
    包的作用
    1、把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
    2、如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
    3、包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
    Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。
    包语句的语法格式为:
    package pkg1[.pkg2[.pkg3…]];
    例如,一个Something.java 文件它的内容
    package net.java.util;
    public class Something{

    }
    那么它的路径应该是 net/java/util/Something.java 这样保存的。 package(包) 的作用是把不同的 java 程序分类保存,更方便的被其他 java 程序调用。
    一个包(package)可以定义为一组相互联系的类型(类、接口、枚举和注释),为这些类型提供访问保护和命名空间管理的功能。
    以下是一些 Java 中的包:
    java.lang-打包基础的类
    java.io-包含输入输出功能的函数
    由于包创建了新的命名空间(namespace),所以不会跟其他包中的任何名字产生命名冲突。使用包这种机制,更容易实现访问控制,并且让定位相关类更加简单。

    创建包
    创建包的时候,你需要为这个包取一个合适的名字。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个包的声明放在这个源文件的开头。
    包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。
    如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中。

    import 关键字
    为了能够使用某一个包的成员,我们需要在 Java 程序中明确导入该包。使用 “import” 语句可完成此功能。
    在 java 源文件中 import 语句应位于 package 语句之后,所有类的定义之前,可以没有,也可以有多条,其语法格式为:
    import package1[.package2…].(classname|*);
    如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。
    类文件中可以包含任意数量的 import 声明。import 声明必须在包声明之后,类声明之前。

    package 的目录结构
    类放在包中会有两种主要的结果:
    包名成为类名的一部分,正如我们前面讨论的一样。
    包名必须与相应的字节码所在的目录结构相吻合。
    通常,一个公司使用它互联网域名的颠倒形式来作为它的包名.例如:互联网域名是 runoob.com,所有的包名都以 com.runoob 开头。包名中的每一个部分对应一个子目录。
    例如:有一个 com.runoob.test 的包,这个包包含一个叫做 Runoob.java 的源文件,那么相应的,应该有如下面的一连串子目录:
    ….\com\runoob\test\Runoob.java
    编译的时候,编译器为包中定义的每个类、接口等类型各创建一个不同的输出文件,输出文件的名字就是这个类型的名字,并加上 .class 作为扩展后缀。

    Java 泛型
    Java 泛型(generics)提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
    泛型方法
    你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。
    下面是定义泛型方法的规则:
    所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的)。
    每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
    类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
    泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。

    public class GenericMethodTest {
      // 泛型方法 printArray
      public static <E> void printArray(E[] inputArray) {
        // 输出数组元素
        for (E element : inputArray) {
          System.out.printf("%s ", element);
        }
        System.out.println();
      }
      public static void main(String args[]) {
        // 创建不同类型数组: Integer, Double 和 Character
        Integer[] intArray = {1, 2, 3, 4, 5};
        Double[] doubleArray = {1.1, 2.2, 3.3, 4.4};
        Character[] charArray = {'H', 'E', 'L', 'L', 'O'};
    
        System.out.println("整型数组元素为:");
        printArray(intArray); // 传递一个整型数组
    
        System.out.println("\n双精度型数组元素为:");
        printArray(doubleArray); // 传递一个双精度型数组
    
        System.out.println("\n字符型数组元素为:");
        printArray(charArray); // 传递一个字符型数组
      }
    }

    有界的类型参数:
    可能有时候,你会想限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。
    要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。
    下面的例子演示了”extends”如何使用在一般意义上的意思”extends”(类)或者”implements”(接口)。该例子中的泛型方法返回三个可比较对象的最大值。

    public class MaximumTest {
      // 比较三个值并返回最大值
      public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
        T max = x; // 假设x是初始最大值
        if (y.compareTo(max) > 0) {
          max = y; // y 更大
        }
        if (z.compareTo(max) > 0) {
          max = z; // 现在 z 更大
        }
        return max; // 返回最大对象
      }
    
      public static void main(String args[]) {
        System.out.printf("%d, %d 和 %d 中最大的数为 %d\n\n", 3, 4, 5, maximum(3, 4, 5));
    
        System.out.printf("%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n", 6.6, 8.8, 7.7, maximum(6.6, 8.8, 7.7));
    
        System.out.printf(
            "%s, %s 和 %s 中最大的数为 %s\n", "pear", "apple", "orange", maximum("pear", "apple", "orange"));
      }
    }

    泛型类
    泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。
    和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。
    如下实例演示了我们如何定义一个泛型类:

    public class Box<T> {
      private T t;
      public void add(T t) {
        this.t = t;
      }
      public T get() {
        return t;
      }
      public static void main(String[] args) {
        Box<Integer> integerBox = new Box<Integer>();
        Box<String> stringBox = new Box<String>();
        integerBox.add(new Integer(10));
        stringBox.add(new String("菜鸟教程"));
        System.out.printf("整型值为 :%d\n\n", integerBox.get());
        System.out.printf("字符串为 :%s\n", stringBox.get());
      }
    }

    类型通配符
    1、类型通配符一般是使用?代替具体的类型参数。例如 List 在逻辑上是List,List 等所有List<具体类型实参>的父类。

    import java.util.*;
    public class GenericTest {
        public static void main(String[] args) {
            List<String> name = new ArrayList<String>();
            List<Integer> age = new ArrayList<Integer>();
            List<Number> number = new ArrayList<Number>();
            name.add("icon");
            age.add(18);
            number.add(314);
            getData(name);
            getData(age);
            getData(number);
       }
       public static void getData(List<?> data) {
          System.out.println("data :" + data.get(0));
       }
    }

    因为getData()方法的参数是List类型的,所以name,age,number都可以作为这个方法的实参,这就是通配符的作用。
    2、类型通配符上限通过形如List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。

    import java.util.*;
    public class GenericTest {
        public static void main(String[] args) {
            List<String> name = new ArrayList<String>();
            List<Integer> age = new ArrayList<Integer>();
            List<Number> number = new ArrayList<Number>();
            name.add("icon");
            age.add(18);
            number.add(314);
            //getUperNumber(name);//1
            getUperNumber(age);//2
            getUperNumber(number);//3
       }
       public static void getData(List<?> data) {
          System.out.println("data :" + data.get(0));
       }
       public static void getUperNumber(List<? extends Number> data) {
              System.out.println("data :" + data.get(0));
       }
    }

    3、类型通配符下限通过形如 List来定义,表示类型只能接受Number及其三层父类类型,如 Object 类型的实例。

    Java 序列化
    Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
    将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
    整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。
    类 ObjectInputStream 和 ObjectOutputStream 是高层次的数据流,它们包含反序列化和序列化对象的方法。
    一个类的对象要想序列化成功,必须满足两个条件:
    该类必须实现 java.io.Serializable 接口。
    该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。
    如果你想知道一个 Java 标准类是否是可序列化的,请查看该类的文档。检验一个类的实例是否能序列化十分简单, 只需要查看该类有没有实现 java.io.Serializable接口。

    public class Employee implements java.io.Serializable
    {
       public String name;
       public String address;
       public transient int SSN;
       public int number;
       public void mailCheck()
       {
          System.out.println("Mailing a check to " + name
                               + " " + address);
       }
    }

    序列化对象
    ObjectOutputStream 类用来序列化一个对象,如下的 SerializeDemo 例子实例化了一个 Employee 对象,并将该对象序列化到一个文件中。
    该程序执行后,就创建了一个名为 employee.ser 文件。该程序没有任何输出,但是你可以通过代码研读来理解程序的作用。
    注意: 当序列化一个对象到文件时, 按照 Java 的标准约定是给文件一个 .ser 扩展名。
    SerializeDemo.java 文件代码:

    import java.io.*;
    public class SerializeDemo
    {
       public static void main(String [] args)
       {
          Employee e = new Employee();
          e.name = "Reyan Ali";
          e.address = "Phokka Kuan, Ambehta Peer";
          e.SSN = 11122333;
          e.number = 101;
          try
          {
             FileOutputStream fileOut =
             new FileOutputStream("/tmp/employee.ser");
             ObjectOutputStream out = new ObjectOutputStream(fileOut);
             out.writeObject(e);
             out.close();
             fileOut.close();
             System.out.printf("Serialized data is saved in /tmp/employee.ser");
          }catch(IOException i)
          {
              i.printStackTrace();
          }
       }
    }

    反序列化对象
    下面的 DeserializeDemo 程序实例了反序列化,/tmp/employee.ser 存储了 Employee 对象。
    DeserializeDemo.java 文件代码:
    “`
    import java.io.*;
    public class DeserializeDemo
    {
    public static void main(String [] args)
    {
    Employee e = null;
    try
    {
    FileInputStream fileIn = new FileInputStream(“/tmp/employee.ser”);
    ObjectInputStream in = new ObjectInputStream(fileIn);
    e = (Employee) in.readObject();
    in.close();
    fileIn.close();
    }catch(IOException i)
    {
    i.printStackTrace();
    return;
    }catch(ClassNotFoundException c)
    {
    System.out.println(“Employee class not found”);
    c.printStackTrace();
    return;
    }
    System.out.println(“Deserialized Employee…”);
    System.out.println(“Name: ” + e.name);
    System.out.println(“Address: ” + e.address);
    System.out.println(“SSN: ” + e.SSN);
    System.out.println(“Number: ” + e.number);
    }
    }
    readObject() 方法中的 try/catch代码块尝试捕获 ClassNotFoundException 异常。对于 JVM 可以反序列化对象,它必须是能够找到字节码的类。如果JVM在反序列化对象的过程中找不到该类,则抛出一个 ClassNotFoundException 异常。
    注意,readObject() 方法的返回值被转化成 Employee 引用。
    当对象被序列化时,属性 SSN 的值为 111222333,但是因为该属性是短暂的,该值没有被发送到输出流。所以反序列化后 Employee 对象的 SSN 属性为 0。

  • 2019-10-18-关于金刚经

    看了宗萨蒋扬钦哲仁波切的《正见》里面关于金刚经的一段话,下面是我的想法:

    我也很喜欢金刚经这一部经文文,有一段时间我把全文背下来了,自我感觉我对这部经文的虔诚心应该是仁波切说的第二个层次,理性的虔诚心。

    但是我总感觉有一些无法说出的东西或者说无法理解的东西在里面,应该是第三个层次的虔诚心才能够理解的吧。

    我始终无法超越理性,虽然我知道世界有可能是不理性的,我也知道理性是很有限制的,在更高层次的智慧面前,我们所谓的理性可能就跟老鼠的理性差不多,但是我觉得理性是我们唯一能够依赖的,超越理性的东西完全无法琢磨无法重复甚至无法思考,没有办法用来做生活的依据。

    也许是我修行不够吧?

    以下是原文:

    现在我想要告诉你们,有三个层次的虔诚心。

    第一种是白痴的虔诚心,愚笨的虔诚心,有神论的虔诚心,畏惧神和地狱的迷信的虔诚心。

    由于这部经关乎灵性,也许你可以用这种方式对待它,不过之后你要超越它而达到另一层次的虔诚心,即理智的虔诚心,不愚蠢的、科学性的虔诚心,基于推理的虔诚心,常识的虔诚心。就好像二加二等于四那样,或者好像如果你煮一颗蛋,它就会被煮熟。

    理性的虔诚心是一种更高的虔诚心,将使你获益匪浅。

    再接下来,你甚至将超越理智的虔诚心,超越科学的虔诚心。你了解到你的所谓的理性是如此有限,和老鼠的智力在同一个层次上,然后你就会看到无尽的显现。

  • 2019-10-07-正见笔记

    正见

    宗萨蒋扬钦哲仁波切

    因与缘:蛋已煮熟,你无法改变它

    “我不相信神的存在”,这种否定的叙述不知道能不能算一种信仰?

    不管你是对自己的宗教还是对自己不信仰宗教感到自豪,信仰在你的生活中都扮演了一个重要的角色。甚至不信也需要信仰:对自己基于多变情绪的逻辑和理性完全盲目的信仰。

    情人眼里出西施

    没有东西是恒常的。

    悉达多步向了探索无常之旅。此刻的他,觉得花费这么多精力于美丽与虚华是很愚蠢的。他批判的并不是美丽与装扮本身,而是相信它们的本质是恒常的信念。

    社会化的快乐定义

    人类不仅仰赖有组织的宗教,也仰赖世俗智慧——甚或政治口号——来获得快乐,去除痛苦。美国总统罗斯福曾说:“如果我必须在正义与和平之间做一个选择,我选择正义。”但究竟是谁的正义?我们应该遵循哪一个人对正义的诠释?极端主义只不过是选择一种正义,而排除所有其他的正义。

    昨日之河流:接受部分逻辑

    可是如果真实存在本来就有依赖性怎么办呢?如果说所有事物都是真实存在的,而且相互依赖的存在,似乎也说得通。

    悉达多发现,要确定某个东西真实存在的唯一办法,就是证明它独立存在,而且不需诠释、不能造作或不会改变。对悉达多而言,我们日常生活中一切似乎能作用的机制,不论是身体的、情感的及概念的,都是由不稳固、不恒常的部分所聚合而成,因此它们随时都在改变。我们可以在惯常的世界中了解这个论点。举例来说,你可以说你在镜中反射的影像不是真实存在的,因为它需要依赖你站在镜子前面才行。如果它是独立存在的,那么你的脸不在镜前时,它也应该存在。类似的,事物要真实或独立地存在,就不能是被造作或被创造的,因为这要依赖制造者。

    领带与情绪的圈套

    这是一个很深刻的比喻。

    “蛇与绳索”是佛教说明空性的经典例子。假设有一个胆小鬼叫杰克,他对蛇有恐惧症。杰克走进一个幽暗的房间,看见一条蛇蜷曲在墙角,顿时惊恐不已。事实上他看到的是一条有花纹的阿玛尼领带,但是由于惊慌,他误认所见的东西,严重到可能把他吓死的程度——被一条不真实存在的蛇给吓死。当他认为那是一条蛇的时候,所经历的痛苦和焦虑,就是佛教徒所说的轮回(samsara),那是一种心理陷阱。幸运的是,杰克的朋友姬儿走进了房间。姬儿沉稳、正常,而且知道杰克以为自己看到了一条蛇。她可以开灯,跟他解释这儿并没有蛇,事实上只是一条领带。当杰克知道自己是安全的,这种解放就是佛教徒所说的涅槃(nirvana)——解脱与自由。但杰克的解放是根基于一个虚假的威胁的消除。本来就没有蛇,本来就没有任何会造成他受苦的东西。

    明了的利益:续集

    了解空性,你会对社会当中忽而建构、忽而解构的一切装饰和信仰失去兴趣——诸如政治系统、科技、世界经济、自由社会、联合国等。你就会像一个成年人,不再对孩童的游戏有多大的兴趣。这么多年来,你曾信赖这些机构,而且相信它们能成就过去制度之所不能。然而这世界并未变得更安全、更愉快或更安定。这并不是说你必须远离社会。了解空性并非表示你变得漠不关心;事实上,相反的,你生起了一种责任感和慈悲心。如果杰克在那儿吼叫、失态,叫骂每个人不要将蛇放在屋子里,而你知道这是来自他的迷惑,就会对他产生同情心。其他人可能不会这么容易原谅他,那么你就可以试着开一点灯。在粗略的层次上而言,你还是会争取你个人的权益,继续上班,在体制内活跃于政治领域,但当情况改变时,不论是对你有利还是不利,你会有备而来。你不会盲目地相信所有的希望和期待会实现,你也就不会被结果所束缚。

    快乐不是目标

    对悉达多而言,究竟的安歇之所,不论是天堂或涅槃,根本就不是一个地方——而是从无明的困惑中解脱出来。如果你一定要指出一个实质的地点,那可能就是你现在坐着的地方。对悉达多而言,那是在印度比哈尔邦的一棵菩提树下,垫了一些干燥忘忧草的平石板上。直到今天,任何人都可到该地参访。悉达多所说的自由是无条件、不受限制的。靠着个人的勇气、智慧和精进,可以在此生中证得。没有任何人不具这种潜能,包括困在地狱道中的众生都一样。

    悉达多的目标并不是要快乐。他的道路并非终究引导到快乐。相反的,它是一条直接的道路,通达痛苦的解脱、妄念和迷惑的解脱。因此涅槃既非快乐也非不快乐——它超越了一切二元的概念。涅槃是寂静。悉达多教导佛法的目的,是要让杰克这种因为怕蛇而痛苦的人完全解脱。这表示杰克必须从以为自己不再受蛇威胁而得到的慰藉中超越出来才行。他必须了解,从一开始就根本无蛇,只有阿玛尼领带而已。悉达多的目标是除去杰克的痛苦,然后帮助他了解,从一开始,就没有本具存在的痛苦之因。

    如同希腊神话中皮格马利翁(Pygmalion)爱上了他自己创作的雕像伽拉泰娅(Galatea)一般,我们自己创造出朋友和敌人,却忘记他们是怎么来的。由于缺乏专注力,我们自己创造的东西变得坚固而真实,而使我们更纠结于其中。当你完全了悟这一切都只是你所创造的,而不只是智识上的了解时,你就自由了。
    快乐的陷阱

    这段话给了我信心。

    当我们看到去除一个世俗的习惯有多难时,证悟似乎是遥不可及的。如果我们连烟都戒不成,怎么能去戒除贪、嗔、痴的习气呢?许多人认为我们必须委托救世主或上师这样的人来替我们做净化的工作,因为我们没有信心能单独做到。但是,只要我们对相互依存的真理有正确的信息,加上一点纪律来应用它的话,这些悲观的看法都不必要了。

    当我们看到去除一个世俗的习惯有多难时,证悟似乎是遥不可及的。如果我们连烟都戒不成,怎么能去戒除贪、嗔、痴的习气呢?许多人认为我们必须委托救世主或上师这样的人来替我们做净化的工作,因为我们没有信心能单独做到。但是,只要我们对相互依存的真理有正确的信息,加上一点纪律来应用它的话,这些悲观的看法都不必要了。

    希望以及本初清净

    无论我们的欲望或者习惯多么的强大,它都是可以改变的。
    经由知识和经验所获得的证悟能够超越怀疑。我们必须完全了解,阻碍证悟的染污和迷惑,并不是固定不动的。纵然我们的障碍看起来既顽强又恒常,事实上它是不稳定的和合现象。明了和合现象的依存性以及能被操作的道理,能引导我们看到它们无常的本质,而且可以让我们确定:它们是可以完全被清除的。

    经由知识和经验所获得的证悟能够超越怀疑。我们必须完全了解,阻碍证悟的染污和迷惑,并不是固定不动的。纵然我们的障碍看起来既顽强又恒常,事实上它是不稳定的和合现象。明了和合现象的依存性以及能被操作的道理,能引导我们看到它们无常的本质,而且可以让我们确定:它们是可以完全被清除的。

    当我们认为自己的本性是愤怒或愚痴的,而对自己达到证悟的能力有怀疑时,我们事实上是认为自己的本性是恒常不净而染污的。但如同杯子上的指印,这些情绪并非我们真实本性的一部分;我们只是从各种不利的情况之中,诸如与不善的人相处,或不了解自己行为的后果等,集结了这些污染物而已。这个本初无染污、自己清净的本性,常被称为佛性。但这些染污和它所带来的情绪已经存在太久了,它强大到成为我们的第二本性,不时地覆盖着我们。难怪我们认为没有希望。

    相信自己可以达到清净的状态,这个信心很重要。

    要重燃希望,在佛教道路上的人可以开始这么想,我的酒杯可以洗干净,我也可以净除我的负面情绪。这和杰克认为蛇应该被移除是类似而稍微幼稚的看法。然而,能够见到事物的本初真性之前,有时候这是必要的准备工作。如果不能够觉受一切现象本来具有的清净,至少相信自己可以达到清净的状态,能帮助我们往前努力。正如杰克想要弄掉那条蛇一般,我们想要去除蔽障,而且有勇气去尝试,是因为我们知道那是可能的。我们只须应用对治方法,来减弱造成染污的因缘,或强化与它相反的因缘,譬如说,生起慈悲心来征服嗔恨。我们热切地洗杯子,是因为相信我们可以有个干净的杯子;同样的,我们热切地想办法去除蔽障,是因为相信我们具有佛性。我们有信心把脏盘子放进洗碗机,是因为知道食物的残渣是可以被清除的。如果有人要我们把木炭洗白,我们就不会有相同的热心和信心了。

    穿过黑暗风暴的一束光

    在这无明、黑暗和迷惑之中,我们如何能探知佛性呢?漂流在汪洋中的水手,第一个希望的征兆来自于穿过黑暗风暴的一束光线。朝向它驶去,他们就会抵达光之源头——灯塔。慈悲就好比从佛性所发出的光。起初,佛性只是超越我们见地的一个概念而已,但如果我们生起慈悲心,终将能趋近它。从一个迷失于贪、嗔、痴黑暗中的人身上,也许很难看出佛性。他们的佛性是如此遥远,我们可能以为根本就不存在。但是即便在最黑暗而暴力的人心中,还是会有慈悲闪现,虽然那可能极为短暂而暗淡。如果能珍惜重视这乍见的光芒,投入更多的能量往光源的方向移动,他们的佛性还是可以被发掘出来的。由于这个原因,人们赞颂慈悲为达到完全去除无明最安全之道。

    循着慈悲心,还有无数的道路可以带领我们证得佛性。即使只是在智识上了解自己和众生本善,也能带我们趋近成就。这就好像我们把珍贵的钻戒放错了地方,但至少知道它还在珠宝箱中某处,不是掉在广大的山野中。

    佛性是我们的本性。

    究竟上我们并非从外在的来源得到证悟。较正确的说法是我们“发觉”了一直都在那儿的证悟。证悟是我们真实本性的一部分。我们的真实本性好似还在模子里尚未取出的一座金质雕像,而这外包装的模子就好比是我们的染污与无明。由于无明和情绪不是我们本性原有的一部分,如同模子不是雕像的一部分,因此有所谓本初清净这种说法。当模子破了,雕像就出现了。当我们的染污清除了,我们真正的佛性就会显现。但很重要的是了解——佛性并非神圣而真实存在的灵魂或本质。

    什么样的感觉?

    佛性是超越理性世界的,所以无法用理性世界的任何概念来描述,如果一定要描述的话,我们只能用否定的方式来描述,比如不生不灭,不垢不净 ,不增不减。

    我们可能还会怀疑,如果既非快乐也非不快乐,那么这证悟到底是什么东西?证悟者如何显现,如何作用?发觉我们的佛性,是什么感受?在佛教的典籍上,对这些问题的回答,通常都说这是超越我们的概念,无法以言语表达的。很多人误以为这是规避问题的狡猾答案。然而,事实上这就是答案。我们的逻辑、语汇和符号是如此有限,连普通的舒解感觉都无法充分地表达;何况是全然舒放的经验,更难用言语来传达给另一个人。如果量子物理学家对他们的理论都难用言语来表达,我们又怎么能冀望用词汇来述说证悟呢?受困于有限的逻辑和语言之中,同时又被情绪紧紧地控制着,在这种状态下,对于证悟,我们只能付诸想象。然而,勤奋努力加上推论逻辑,我们或可得到近似的答案,恰如你见到山顶有烟,就能推测应该有火一般。利用现有的能力,我们可以开始了解并接受,蔽障来自可以被操作的因缘,而且终究能被净除。想象没有染污情绪和负面性的状态,是了解证悟本性的第一步。假设你现在正在头痛,你即刻的愿望就是将它消除。这是可能的,因为你知道头痛不是你天生的一部分。接着你试图去了解为何头会痛——譬如说,缺乏睡眠。然后你用适当的疗法来去除头痛,诸如服用阿司匹林或倒下来睡个觉等。

    苦集灭道。

    在瓦拉纳西初转法轮时,悉达多就教导了这四个步骤,就是大家熟知的四圣谛:了知苦,抛弃苦之因,修息苦之道,了知苦可灭。

    这个例子不太恰当,因为有些人吃冰淇淋没有什么焦虑的,比如孩子或者不担心自己长胖的人。

    对一个正在高高兴兴舔尝冰淇淋的人,很难让他相信他正在受苦。然后,他才想起了医生警告他要降低胆固醇和减轻体重的事。如果你能仔细探索这个状似愉快的经验,从他开始渴望吃一个冰淇淋,一直到他对肥胖和胆固醇的担心,你会发现他一直都处在焦虑之中。

    问题是人群中土包子居多。

    在佛教中,证悟者并不是由其超自然能力(如飞行)或某种身体特征(如第三眼)来断定的。虽然佛陀本人常被描述为庄严殊胜、身呈金色、手柔软、具帝王相,但这些形容主要对无知的土包子或像杰克一般的人才有吸引力。

    从觉悟者的角度来看,我们认为这些是奇迹这个现象,就说明我们是还没有觉悟的土包子。

    他最伟大的成就是了知了实相,因为了知实相能让我们彻底从痛苦中解脱。这才是真正的奇迹。佛陀和我们看到一样的生老病死,但他致力于找寻其根本原因,这也是一个奇迹。他证得一切和合事物皆无常,是他究竟的胜利。他并非炫耀他打败了一个外在的敌人,而是发现了真正的敌人是攀缘的我执;而击败我执,比一切真实或想象的超自然能力,都是更大的奇迹。

    超越时间与空间的好处

    没有自我,就没有痛苦。

    究竟上,他教导大家,经由解构自我,痛苦可以从根断除,因为如果没有自我,就没有痛苦。

    见地是最终的参考点

    个人感觉虽然空性无法用语言描述,但是祥和与宁静可能是最接近于空性的。个人想象中超越了一切概念包括时间空间之后的那种状态应该是祥和与宁静的。

    每一个宗教对于它们的象征和修持都有深刻的解释——为何不吃猪肉、不吃虾,为何必须剃头或不准剃头。然而在这些无尽的可与不可之间,每个宗教必定有个基本的见地,而见地才是最为重要的。如同所有其他的宗教,佛教也有一些行为规范,但对佛教徒而言,这不是最重要的。如果法国政府突然决定禁止佛教徒在香榭丽舍大道上穿着藏红色袍子,佛教徒应该不会有异议。事实上,如果在公众场合中不穿藏红色袍子能增进祥和与宁静,佛教徒应该会乐于遵从,因为祥和与宁静接近佛教的核心。决定行为是否恰当,最终的参考点是见地。评估行为,要看它和自己的见地是否相配。

    佛教用语是诸行无常,有漏皆苦,诸法无我,涅盘寂静。不过这是古代用语还是作者这里比较容易懂。

    佛教的一切方法都可以用四法印来解释——一切和合现象皆无常、一切情绪皆苦、一切事物无自性,以及涅槃超越概念。佛教经典所提倡的每一言行,都是基于前面所讨论过的这四个见地或四个真谛。

    深入了解业报、清净和非暴力

    这里我想说业和轮回这两个概念其实是四法印的推论而已,说的更直白一点,这两个概念有可能是多余甚至错误的。四法印的道理是显而易见的,当然有的时候叫三法印,因为诸行无常,诸法无我,涅盘寂静这三个似乎是不言自明的,诸行无常说一切和合事物都是无常的,我们知道宇宙都是有寿命的,没有哪个事物是永恒不变的;诸法无我是说一切事物都没有自性,我们知道一切事物都是由更小的部分组成的,没有哪个部分是永恒不变的,永远是那个事物的一部分的;涅盘寂静是说涅磐超越了一切概念,从而不可描述,我们从逻辑上也知道涅磐或者说是空性是超越语言文字时间空间的,所以是不可描述的。

    业这个概念,无可否认地是佛教的商标,它也包含在这四个真谛之中。当因缘和合而且没有障碍时,结果就会出现。结果就是业。业是由意识(心或自我)所集合而成的。如果这个自我因贪爱或嗔恨而行动,就会产生恶业。如果念头或行为的动机是基于慈爱、忍耐和希望他人快乐的原因,就会产生善业。然而,动机、行为和业果本质上皆是如梦如幻的。超越业报,不论善的或恶的业报,就是涅槃。任何不是根基于这四种见地的所谓善行,都只是正义(righteousness)而已,它不是悉达多的究竟之道。

    佛教徒实践非暴力,并非只是微笑退缩或温驯体贴而已。暴力的基本原因来自于执着极端的想法,例如公平或道德。这种执着通常来自于采取二元见地的习惯,例如善与恶、美与丑、道德与不道德等。个人僵化的自我正义感占据了所有的空间,以至于容不下对他人的同情心,理智因而丧失;如果能了解所有这些见地或价值观以及鼓吹它们的人都是和合而且无常的,就能防止暴力。当你没有我执,不执着自我,就完全没有理由使用暴力。如果能了解到敌人是被他们自己的无明和嗔恨等强大的影响力所控制,知道他们是陷于习气之中而无法自拔,我们就会比较容易原谅他们令人恼怒的行为。相同的,如果有精神病患者侮辱你,你不会有理由生气。如果我们能够超越相信二元现象的极端,就能超越暴力之因。
    四法印:无法分割的整体

    这个比喻很好,把科学和佛教联系起来了,确实有相似之处,科学里面没有亵渎这个概念,科学里面的任何质疑都可以接受,只要你能提出有力的证据。类似佛教里面也是提倡智慧的,不能盲目的相信,佛陀就曾经说过不能是因为是我说的你们就相信,不能因为书上说的你们就相信,不能因为名人说的你们就相信,一定要亲自去思考体验才能相信。科学和佛教之所以有这种自信是因为他们的基础都建立于很可靠的事实之上,科学不用说,完全是基于实验和逻辑的,佛教的四法印也是很可靠的,可以验证的,符合逻辑的。

    为了沟通起见,我们可以说这四种见地是佛教的主干。我们称之为“真谛”,因为它们是单纯的事实。没有人制造了它们:它们不是佛陀神秘的天启,也不是佛陀开始教法以后才变成的事实。依照这些原则生活,并非仪式,也非技巧;它们不属于伦理或道德,也无法被专属或独享。在佛教中,没有所谓的“不信神的异端”或“亵渎上帝者”,因为不存在你必须忠诚的对象,也没有可以污辱或怀疑的对象。然而,对不觉知或不相信这四种真谛的人,佛教徒认为他们是无明的。然而无明并非拿来作为道德判定的原因。如果有人不相信人类已经登陆月球,或者认为地球是扁平的,科学家只会说他是无知,而不会说他亵渎科学。相同的,如果他不相信这四种真谛,他并非异端。事实上,如果有人能证明这四真谛的逻辑是错误的,证明执着自我并不痛苦,或有些元素并非无常,那么佛教徒会很愿意去遵从那条道路。因为我们所追寻的是证悟,而证悟意指对真相的了悟。然而,至少到今天,多少世纪以来,未曾出现过任何否定这四个真谛的证明。

    修持祥和

    这段话我不敢苟同,现代世界在任何方面都比古代要好,人们的寿命在提高,生活水平在提高,战争在减少,就连佛法的传播也得益于现代科技水平的提高。整体来说,人类变得更加文明。

    现代世俗世界并没有做得更好,反而更糟。现代世界有什么变得更好的?科技的主要效应之一,就是更快速地摧毁这世界。有人相信,在地球上的每一种生命系统和每一种维生系统,都在衰落之中。

    不杀生是所有戒律里面的第一条。作为居士,我们要守五戒,不杀生,不偷盗,不淫邪,不妄语,不饮酒。这是任何一个佛教徒都应该遵守的。这五戒是最根本的戒律。

    作为佛教徒,你应该坚守这个原则:佛教徒绝不以佛教之名参与或鼓励流血。你连只小虫都不能杀,更何况人。设若你知道某个佛教徒或团体这么做,那么,作为佛教徒,你必须抗议并且谴责他们。如果你保持缄默,你不只是鼓励他们,基本上你就和他们一伙了。你就不是佛教徒。

    点评

    又是一本值得推荐的书,宗萨蒋阳钦哲仁波切的书,真的是每一本都值得读。佛教说起来其实很简单,就是四句话,诸行无常,有漏皆苦,诸法无我,涅槃寂静;但是做起来,有84000法门,不同的人适合不同的法门,终极的目的是涅槃,其实前面三法印都是为了最后这一条,我们之所以要认识诸行无常,有漏皆苦,诸法无我,目的就在于到达涅磐,也就是超越一切烦恼,超越时间空间,超越一切的概念,超越理性。

  • 2019-10-01-关于爱国

    几个朋友在群里面讨论为何爱国,我发表了一下我的看法。

    爱国本来是一种很朴素的感情,你生在这里,长在这里,父母亲人都在这里,所以你对这里有感情,就像动物对自己的窝有感情一样。

    所以爱国主义教育本来是一种多余的东西。爱国主义的目的在于把国家和政府混在一起,把对国家的感情洗脑成对政府的感情。

    比如国庆节,其实应该叫做党建国庆节才对,我们的国家何止70岁,从北京猿人算起,几十万岁;从现代人类算起,至少1万多岁;从中国文明算起,也有几千岁了;只有从共产党建国算起,才是70岁。

    即使是那种最朴素的乡土的感情,也不是天然正确的。不同的人对乡土的感情不一样,有的人感情深,有的人感情浅。像我自己对湖北老家就没有太多感情,对上海也一样,我觉得在现代社会在哪个城市生活都差不多。

    有一些人可能会说,国家保证你的安全,难道你不应该爱国吗?这就是逻辑不好的例子,其实他们这里说的是政府,保卫我们安全的,是军队和政府。但是别忘了政府拿走了gdp的30%,中国政府税收水平是全球第二高的,按照社会契约论,你拿走那么多钱,不应该保证安全吗?即使是黑社会保护费,也会保证你安全的。

  • 2019-10-04-关于理想主义者

    昨天晚上一个朋友问我关于老罗的看法,问我到七八十岁回想起来还会不会讨厌老罗。

    下面是我的回答:

    我:我没讨厌老罗,只是觉得他是一个伪理想主义者。最多是不喜欢。

    朋友:真理想主义者是什么样子呢?有没有谁是?

    我:我不确定。也许是乔布斯,扎克布格那种吧。就是比较单纯,真诚的相信自己所说的。不是通过假装理想主义者来实现自己的商业目的或者其他目的。纯属个人感觉,就像乔布斯他真的相信自己能做出最好的手机。就像扎克伯格,相信自己能够把世界上每个人都连接起来。而老罗给我的感觉,他其实并不相信自己能做出最好的手机,他其实知道自己一直是在吹牛逼,他又不傻。但是他就通过包装成一个理想主义者,什么工匠精神,什么认认真真,加上吹牛逼来吸引一群粉丝。不得不说理想主义者是很有吸引力的,因为每个人都在现实里面挣扎,看到一个人不为现实所动,坚持自己的理想,这是很有感染力的。不管他是不是装出来的。

    朋友:中国有谁是吗?理想主义者。

    我:中国的当代的我真一时想不起来,古代的,商鞅?王安石?王莽?但是古代的,只是表现出来的理想主义,实际上是不是不好说。真正的理想主义者,经常会表现出一种悲剧的色彩。因为他们坚信的东西有可能是错的。

    讨论完了之后,我又去搜索了一下理想主义。结果发现维基百科上的理想主义实际上是唯心主义,跟我想的完全不同。

    唯心论(英语:idealism)或唯心主义、理想主义、理念论或观念论,在哲学中是一个提出心灵是基本之存在的哲学理论。

    百度百科上有一个理想主义者的词条,上面写着理想主义者是指善于运用直觉去认识世界,运用情感去对世界作出判断的人。理想主义是高于现实,并能调整现实的一种思想倾向。

    所以学术界的理想主义可能跟理想没啥关系。而我理解的理想主义者是指坚持理想,不为现实所动的人。

    我看到知乎上一个链接怎么判断一个人是不是理想主义者?下面的回答倒是跟我的理解很像。

    我又仔细搜索了一下维基百科,根本没有idealist这个词条,看来理想主义者这个词可能又是一个望文生义的结果。

  • 2019-10-02-双色球数字概率分析

    双色球的规则是红色球从数字1到33中选出6个,蓝色球从1到16中选出1个。

    假定福彩中心没有作弊,概率完全随机,蓝色球随机没有办法分析,所以每个数字出现概率为1/16。

    这里我们来分析一下红色球。红色球的结果可能有C(33,6) = 1107568种(C33)

    我们把红色球出现的号码从小到大排列,分别叫做红1到红6.

    红1最少值是1,最大值是28.

    红1为1的可能性是先选一个1出来,其余的32个球中选出5个来的所有可能性。
    C(32,5)/C(33,6) = 201376/1107568 = 18.18%

    红1为2的可能性是先排除号码1,然后选一个2出来,其余的31个球中选出5个来的所有可能性。
    C(31,5)/C(33,6) = 169911/1107568 = 15.34%

    依次类推,
    红1为3的概率是
    C(30,5)/C(33,6) = 142506/1107568 = 12.86%

    继续分析规律。

    红2最少值是2,最大值是29.

    红2为2的可能性是先选一个1号球当红1,然后选一个2号球当红2,其余31个球中选4个出来的所有可能性。
    C(31,4)/C(33,6) = 31465/1107568 = 2.84%

    红2为3的可能性是先选一个1号球或者2号球当红1,然后选一个3号球当红2,其余30个球中选4个出来的所有可能性。
    C(2,1)C(30,4)/C(33,6) = 227405/1107568 = 4.95%

    所以通用的概率公式就是第n个球等于i的可能性除以总的可能性。

    C(i-1,n-1)*C(33-i,6-n)/C(33,6)

    写成java代码就是:

    // LotteryChance.java
    // javac LotteryChance.java
    // java LotteryChance
    public class LotteryChance {
    
        public static double factorial(int n) {
            double[] f = new double[n+1];
            f[0] = f[1] = 1;
            for (int i = 2; i <= n; i++)
                f[i] = f[i-1] * i;
            return f[n];
        }
    
        public static double combination(int n, int r){
            if(n == 0 || r == 0 || n == r) return 1;
            return Math.round(factorial(n)/(factorial(r)*factorial(n-r)));
        }
    
        public static void main(String[] args) {
            double all = 1107568; // combination(33,6);
            double[][] chance = new double[6][28];
            for(int n = 1; n < 7; n++){
                System.out.println("the "+ n +"th number:");
                for(int i = n; i < 34-(6-n); i++) {
                    chance[n-1][i-n] = combination((i-1), (n-1))*combination((33-i), (6-n))/all;
                    System.out.println("the chance = "+i+" is : "+100*chance[n-1][i-n]+"%.");
                }
            }
        }
    }

    输出结果是:

    the 1th number:
    the chance = 1 is : 18.181818181818183%.
    the chance = 2 is : 15.340909090909092%.
    the chance = 3 is : 12.866568914956012%.
    the chance = 4 is : 10.722140762463344%.
    the chance = 5 is : 8.87349580341794%.
    the chance = 6 is : 7.288942981379021%.
    the chance = 7 is : 5.939138725568092%.
    the chance = 8 is : 4.796996662958843%.
    the chance = 9 is : 3.8375973303670743%.
    the chance = 10 is : 3.0380978865406005%.
    the chance = 11 is : 2.3776418242491655%.
    the chance = 12 is : 1.8372686823743551%.
    the chance = 13 is : 1.3998237579995088%.
    the chance = 14 is : 1.0498678184996315%.
    the chance = 15 is : 0.7735868136313075%.
    the chance = 16 is : 0.558701587622611%.
    the chance = 17 is : 0.3943775912630195%.
    the chance = 18 is : 0.2711345939933259%.
    the chance = 19 is : 0.1807563959955506%.
    the chance = 20 is : 0.11620054028285397%.
    the chance = 21 is : 0.0715080247894486%.
    the chance = 22 is : 0.041713014460511684%.
    the chance = 23 is : 0.02275255334209728%.
    the chance = 24 is : 0.01137627667104864%.
    the chance = 25 is : 0.005056122964910506%.
    the chance = 26 is : 0.00189604611184144%.
    the chance = 27 is : 5.417274605261257E-4%.
    the chance = 28 is : 9.028791008768762E-5%.
    the 2th number:
    the chance = 2 is : 2.840909090909091%.
    the chance = 3 is : 4.948680351906158%.
    the chance = 4 is : 6.433284457478006%.
    the chance = 5 is : 7.394579836181616%.
    the chance = 6 is : 7.9227641101945885%.
    the chance = 7 is : 8.098825534865579%.
    the chance = 8 is : 7.994994438264738%.
    the chance = 9 is : 7.675194660734149%.
    the chance = 10 is : 7.195494994438264%.
    the chance = 11 is : 6.604560622914349%.
    the chance = 12 is : 5.944104560622914%.
    the chance = 13 is : 5.2493390924981576%.
    the chance = 14 is : 4.549427213498404%.
    the chance = 15 is : 3.8679340681565373%.
    the chance = 16 is : 3.2232783901304476%.
    the chance = 17 is : 2.6291839417534635%.
    the chance = 18 is : 2.0951309535847913%.
    the chance = 19 is : 1.6268075639599553%.
    the chance = 20 is : 1.2265612585412362%.
    the chance = 21 is : 0.8938503098681074%.
    the chance = 22 is : 0.6256952169076753%.
    the chance = 23 is : 0.41713014460511677%.
    the chance = 24 is : 0.2616543634341187%.
    the chance = 25 is : 0.1516836889473152%.
    the chance = 26 is : 0.07900192132672666%.
    the chance = 27 is : 0.03521228493419817%.
    the chance = 28 is : 0.012188867861837828%.
    the chance = 29 is : 0.002528061482455253%.
    the 3th number:
    the chance = 3 is : 0.36656891495601174%.
    the chance = 4 is : 0.9897360703812317%.
    the chance = 5 is : 1.7746991606835878%.
    the chance = 6 is : 2.640921370064863%.
    the chance = 7 is : 3.5212284934198173%.
    the chance = 8 is : 4.360906057235312%.
    the chance = 9 is : 5.116796440489433%.
    the chance = 10 is : 5.756395995550612%.
    the chance = 11 is : 6.256952169076752%.
    the chance = 12 is : 6.604560622914349%.
    the chance = 13 is : 6.793262354997616%.
    the chance = 14 is : 6.824140820247606%.
    the chance = 15 is : 6.704419051471332%.
    the chance = 16 is : 6.446556780260895%.
    the chance = 17 is : 6.0673475578926075%.
    the chance = 18 is : 5.5870158762261095%.
    the chance = 19 is : 5.028314288603499%.
    the chance = 20 is : 4.415620530748451%.
    the chance = 21 is : 3.774034641665342%.
    the chance = 22 is : 3.128476084538376%.
    the chance = 23 is : 2.502780867630701%.
    the chance = 24 is : 1.9187986651835371%.
    the chance = 25 is : 1.3954899383152999%.
    the chance = 26 is : 0.9480230559207201%.
    the chance = 27 is : 0.5868714155699695%.
    the chance = 28 is : 0.31691056440778353%.
    the chance = 29 is : 0.13651532005258368%.
    the chance = 30 is : 0.036656891495601175%.
    the 4th number:
    the chance = 4 is : 0.036656891495601175%.
    the chance = 5 is : 0.13651532005258368%.
    the chance = 6 is : 0.31691056440778353%.
    the chance = 7 is : 0.5868714155699695%.
    the chance = 8 is : 0.9480230559207201%.
    the chance = 9 is : 1.3954899383152999%.
    the chance = 10 is : 1.9187986651835371%.
    the chance = 11 is : 2.502780867630701%.
    the chance = 12 is : 3.128476084538376%.
    the chance = 13 is : 3.774034641665342%.
    the chance = 14 is : 4.415620530748451%.
    the chance = 15 is : 5.028314288603499%.
    the chance = 16 is : 5.5870158762261095%.
    the chance = 17 is : 6.0673475578926075%.
    the chance = 18 is : 6.446556780260895%.
    the chance = 19 is : 6.704419051471332%.
    the chance = 20 is : 6.824140820247606%.
    the chance = 21 is : 6.793262354997616%.
    the chance = 22 is : 6.604560622914349%.
    the chance = 23 is : 6.256952169076752%.
    the chance = 24 is : 5.756395995550612%.
    the chance = 25 is : 5.116796440489433%.
    the chance = 26 is : 4.360906057235312%.
    the chance = 27 is : 3.5212284934198173%.
    the chance = 28 is : 2.640921370064863%.
    the chance = 29 is : 1.7746991606835878%.
    the chance = 30 is : 0.9897360703812317%.
    the chance = 31 is : 0.36656891495601174%.
    the 5th number:
    the chance = 5 is : 0.002528061482455253%.
    the chance = 6 is : 0.012188867861837828%.
    the chance = 7 is : 0.03521228493419817%.
    the chance = 8 is : 0.07900192132672666%.
    the chance = 9 is : 0.1516836889473152%.
    the chance = 10 is : 0.2616543634341187%.
    the chance = 11 is : 0.41713014460511677%.
    the chance = 12 is : 0.6256952169076753%.
    the chance = 13 is : 0.8938503098681074%.
    the chance = 14 is : 1.2265612585412362%.
    the chance = 15 is : 1.6268075639599553%.
    the chance = 16 is : 2.0951309535847913%.
    the chance = 17 is : 2.6291839417534635%.
    the chance = 18 is : 3.2232783901304476%.
    the chance = 19 is : 3.8679340681565373%.
    the chance = 20 is : 4.549427213498404%.
    the chance = 21 is : 5.2493390924981576%.
    the chance = 22 is : 5.944104560622914%.
    the chance = 23 is : 6.604560622914349%.
    the chance = 24 is : 7.195494994438264%.
    the chance = 25 is : 7.675194660734149%.
    the chance = 26 is : 7.994994438264738%.
    the chance = 27 is : 8.098825534865579%.
    the chance = 28 is : 7.9227641101945885%.
    the chance = 29 is : 7.394579836181616%.
    the chance = 30 is : 6.433284457478006%.
    the chance = 31 is : 4.948680351906158%.
    the chance = 32 is : 2.840909090909091%.
    the 6th number:
    the chance = 6 is : 9.028791008768762E-5%.
    the chance = 7 is : 5.417274605261257E-4%.
    the chance = 8 is : 0.00189604611184144%.
    the chance = 9 is : 0.005056122964910506%.
    the chance = 10 is : 0.01137627667104864%.
    the chance = 11 is : 0.02275255334209728%.
    the chance = 12 is : 0.041713014460511684%.
    the chance = 13 is : 0.0715080247894486%.
    the chance = 14 is : 0.11620054028285397%.
    the chance = 15 is : 0.1807563959955506%.
    the chance = 16 is : 0.2711345939933259%.
    the chance = 17 is : 0.3943775912630195%.
    the chance = 18 is : 0.558701587622611%.
    the chance = 19 is : 0.7735868136313075%.
    the chance = 20 is : 1.0498678184996315%.
    the chance = 21 is : 1.3998237579995088%.
    the chance = 22 is : 1.8372686823743551%.
    the chance = 23 is : 2.3776418242491655%.
    the chance = 24 is : 3.0380978865406005%.
    the chance = 25 is : 3.8375973303670743%.
    the chance = 26 is : 4.796996662958843%.
    the chance = 27 is : 5.939138725568092%.
    the chance = 28 is : 7.288942981379021%.
    the chance = 29 is : 8.87349580341794%.
    the chance = 30 is : 10.722140762463344%.
    the chance = 31 is : 12.866568914956012%.
    the chance = 32 is : 15.340909090909092%.
    the chance = 33 is : 18.181818181818183%.

    从结果可以看出,
    红1最有可能出现的数字是1-4, 最大可能性是1.
    红2最有可能出现的数字是6-9, 最大可能性是7.
    红3最有可能出现的数字是12-15, 最大可能性是14.
    红4最有可能出现的数字是19-22, 最大可能性是20.
    红5最有可能出现的数字是25-28, 最大可能性是27.
    红6最有可能出现的数字是30-33, 最大可能性是33.

    为了验证我的结果对不对,我写了一个程序来模拟双色球开奖,同时统计红1到红六出现的次数。
    代码如下:

    // Lottery.java
    // javac Lottery.java
    // java Lottery 100000000
    import java.util.Arrays;
    public class Lottery {
        public static int mostFrequent(int[] arr) {
            int max = 0;
            int f = 0;
            for(int i = 0; i< arr.length;i++) {
                if(arr[i] > max) {
                    max = arr[i];
                    f = i+1;
                }
            }
            return f;
        }
        public static void main(String[] args) {
    
            if(args.length < 1)
            {
                System.out.println("Proper Usage is: java Lottery 1000(or how many you want to try).");
                System.exit(0);
            }
            int n = Integer.parseInt(args[0]);    // choose this many elements
            int[] red = new int[33];
            int[] first = new int[34];
            int[] second = new int[34];
            int[] third = new int[34];
            int[] fourth = new int[34];
            int[] fifth = new int[34];
            int[] sixth = new int[34];
            for (int i = 0; i < 33; i++) {
                red[i] = i+1;
            }
            for(int c = 0; c < n; c++){
                for (int i = 0; i < 6; i++) {
                    int r = i + (int)(Math.random()*(33-i));
                    int t = red[r];
                    red[r] = red[i];
                    red[i] = t;
                }
                int[] result = new int[6];
                for (int i = 0; i<6; i++){
                    result[i] = red[i];
                }
                Arrays.sort(result);
                first[result[0]-1]++;
                second[result[1]-1]++;
                third[result[2]-1]++;
                fourth[result[3]-1]++;
                fifth[result[4]-1]++;
                sixth[result[5]-1]++;
                if(args.length > 1){
                    for (int i = 0; i < 6; i++){
                        System.out.print(result[i] + " ");
                    }
                    System.out.println();
                    System.out.printf("red : %s\n", Arrays.toString(red));
                }
            }
            System.out.printf("first : %s\n", Arrays.toString(first));
            System.out.println(mostFrequent(first));
            System.out.printf("second : %s\n", Arrays.toString(second));
            System.out.println(mostFrequent(second));
            System.out.printf("third : %s\n", Arrays.toString(third));
            System.out.println(mostFrequent(third));
            System.out.printf("fourth : %s\n", Arrays.toString(fourth));
            System.out.println(mostFrequent(fourth));
            System.out.printf("fifth : %s\n", Arrays.toString(fifth));
            System.out.println(mostFrequent(fifth));
            System.out.printf("sixth : %s\n", Arrays.toString(sixth));
            System.out.println(mostFrequent(sixth));
        }
    }

    我执行java Lottery 100000000就是开奖一亿次, 结果如下:
    first : [18181415, 15341925, 12865221, 10724122, 8871170, 7285535, 5942311, 4799328, 3837023, 3036659, 2376066, 1835980, 1400676,
    1050389, 773613, 559635, 395151, 271204, 180914, 116457, 71486, 41806, 23020, 11295, 5043, 1939, 532, 85, 0, 0, 0, 0, 0, 0]
    1
    second : [0, 2840458, 4946818, 6432150, 7396106, 7919007, 8098429, 7991389, 7676025, 7199131, 6605295, 5949171, 5248256, 4550103,
    3869756, 3221894, 2628698, 2095361, 1625266, 1227908, 893273, 626626, 417263, 262032, 151108, 78369, 35414, 12189, 2505, 0, 0, 0,
    0, 0]
    7
    third : [0, 0, 365942, 988819, 1774042, 2639593, 3518744, 4361205, 5117636, 5755000, 6255513, 6602839, 6793753, 6830798, 6704630,
    6444648, 6068836, 5590065, 5026655, 4413666, 3775953, 3128141, 2504301, 1918079, 1395028, 948685, 587462, 317071, 136088, 36808, 0
    , 0, 0, 0]
    14
    fourth : [0, 0, 0, 36697, 136690, 316144, 586649, 947804, 1394881, 1916290, 2501275, 3128912, 3773469, 4415433, 5028612, 5588366,
    6066877, 6445842, 6701330, 6826578, 6790441, 6606725, 6259293, 5757620, 5117888, 4361739, 3521883, 2643699, 1773521, 988768, 36657
    4, 0, 0, 0]
    20
    fifth : [0, 0, 0, 0, 2529, 12318, 35027, 78985, 151410, 262359, 415583, 624312, 894846, 1226446, 1626489, 2094632, 2629957, 322277
    7, 3867689, 4549473, 5247548, 5943771, 6600351, 7195273, 7673459, 7997888, 8101801, 7921915, 7397245, 6433408, 4949122, 2843387, 0
    , 0]
    27
    sixth : [0, 0, 0, 0, 0, 87, 553, 1906, 4962, 11356, 22608, 41828, 71612, 115976, 181000, 271124, 394621, 559229, 773541, 1050827,
    1401171, 1835353, 2378715, 3035969, 3838368, 4797255, 5937331, 7288578, 8877601, 10721731, 12864228, 15335628, 18186842, 0]
    33

    可以看到跟我上面的理论分析出来的结果一致。
    上面结果里面first后面中括号里就是开奖一亿里面,
    红1分别等于1到33的次数,当然红1最小等于1,最大等于28,不可能等于33.
    可以看到出现的次数基本上等于我们上面理论分析的概率乘以一亿。
    比如红1等于1的概率是18.181818181818183%,乘以一亿,理论出现次数是18181818,
    这里的出现开奖结果次数是18181415,基本吻合。

    同时出现次数也跟理论预言的一样,
    红1出现次数最多的就是1。
    红2出现次数最多的就是7。
    红3出现次数最多的就是14。
    红4出现次数最多的就是20。
    红5出现次数最多的就是27。
    红6出现次数最多的就是33。

    至次,我们得到了双色球各个数字出现的概率。

    进一步分析,所以红色球中奖概率最大的号码是什么呢?显然就是1,7,14,20,27,33。这个号码的中奖概率有多大呢?把上面的六个概率相乘,可以得出

    18.181818181818183%*8.098825534865579%*6.824140820247606%*6.824140820247606%*8.098825534865579%*18.181818181818183%=1.0097518751278756E-6

    所以这个号码的数学期望值是多少呢?
    1.0097518751278756E-6*5000000/16=0.31554746097,

    也就是0.31元,三毛钱,所以就算彩票没有作弊,你用两块钱买到的预期收益也只有三毛一。这就是彩票是一种智商税这种说法的由来。

  • 2019-09-22-关于量子计算

    看到一篇关于量子计算机的文章
    https://mp.weixin.qq.com/s/OZ6yVl2I2TSwOEk2HnnyNQ

    下面是我的看法:
    量子计算还没到可用的阶段。你可以去看原文:
    https://www.ft.com/content/b9bb4e54-dbc1-11e9-8f9b-77216ebe1f17
    不过要翻墙。

    我在读研的时候,有个法国教授就给我们讲了量子计算的原理,他说量子计算只能解决特定的问题,而且很难控制,从这篇文章里面,那些问题依然没有解决。

    可以参考新浪科技上这篇文章:可以参考新浪科技上这篇文章:https://tech.sina.com.cn/d/i/2019-03-26/doc-ihsxncvh5553617.shtml

    Google的那个量子霸权都有疑问,更别说通用量子计算机了。

    现在阶段的量子计算基本上都是在炒作,骗骗政府经费什么的,离成熟技术的标准还很远。

    量子计算跟可控核聚变有点像,可能永远都离商用还需要三十年。氢弹爆炸之后,科学家就说可控核聚变应该三十年就能实现了,结果到现在还是需要三十年。。

  • 2019-09-24-关于对富人征税

    看到一篇文章,里面写比尔盖茨愿意捐出全部财产,并且号召对富人征税。

    https://mp.weixin.qq.com/s/r4l_dmToU-6P6h7Cwv4SsQ

    下面是我的看法:

    看不见的后果很少有人能想到。

    关注短期的利益是大多数人的天性,在中国均贫富的口号已经响了几千年,王侯将相宁有种乎。

    但是允许公开打劫富人的后果就是所有人都是穷人。

    可悲的是有些穷人想就算所有人都是穷人,也没什么了不起,说不定更好,起码可以心里平衡一点。

    这也是人性。

    像张是之这样的奥派经济学者关心的始终是长期的利益,关心的是整个经济体运行的效率,关心的是资源怎么配置更合理。

    问题是大多数人可能并不关心有没有效率,只关心公平不公平,他们可能宁愿牺牲效率去换一个看上去更公平的结果。

    这个过程就像那个童话故事里面,两个小狗捡到一块肉,然后让狐狸来分配以获得公平,狐狸在两块肉中间,左咬一口右咬一口,最后两只小狗吃到嘴里的都是两块很小的肉,大部分都被狐狸给吃了。

    可悲的是大多数人宁可吃的肉更少,也要大家吃的肉都差不多大小,尽管过去40年中国的经验告诉我们,只有允许大家吃的肉不一样多,总体来说每个人吃的肉才能更多。

    也许只有真正挨过饿的那一代人才能体会到肉少日子真的很难受,可惜那一代人已经老了,他们的经验没有办法传递给他们的后代,因为他们的后代没有经历过饿肚子的日子。

    每个人都是从婴儿开始长大发育,这真是一个悲惨的事实。

    因为这意味着”历史书上记载三年自然灾害(胡乱折腾),中国饿死了1000万人”这件事,对他们来说只是一行数字而已,他们无法体会挨饿的感受,也无法体会身边的亲人饿死的感受。

  • 2019-09-23-观念与经验

    朋友问我“突破观念与经验的人都是伟人”这句话有没有道理。

    下面是我的回答:

    有道理,因为破除对一切观念的执着就是佛陀。

    佛陀的观点是这样子的,我们所有看到的听到的想到的一切加上我们自己构成了一部叫做轮回的电影,我们在这部电影中无法自拔,我们是如此的相信我们看到的听到的想到的一切是真实的,以至于我们完全不可能意识到我们是在一部电影里面。

    我们入戏太深,所以完全以戏中的角色来代替自己,完全的相信戏中角色看到的听到的想到的,而戏中角色看到的听到的想到的就是我们所谓的观念和经验。

    轮回中的一切都是在表演,我们表演痛苦和快乐,我们表演拥抱与背叛,我们表演爱与恨,我们表演得与失。

    我们表演生老病死然后再继续下一场表演。

    我们入戏太深,完全不知道我们在表演。

    佛陀就是那个看透了我们是在表演的人,他教导我们怎样从这个轮回的表演中脱离出来。

    脱离轮回的关键就在于破除对表演中一切观念的执着。

    从佛陀的角度来讲,没有善与恶,没有对与错,没有苦与乐,没有得与失。

    所有这些二元对立的观念都只存在于轮回中。

    而佛陀是超越了脱离了这些观念的人。

  • 2019-09-19-关于刺激消费

    我经常在电视里面听到一个词叫做刺激消费,动不动国家就出台什么政策来刺激消费。

    我一直觉得这个说法很奇怪,今天就来聊一聊刺激消费的问题。

    首先,消费需要刺激吗?

    消费是人的本能,根本不需要刺激。

    人只要活着就会消费,没有人可以例外。

    国家出台政策来刺激消费,其实准确的来说,是让人们把手中的钞票用掉。

    当然,这一点国家是很成功的,因为所有人都知道手中的钞票会越来越不值钱。

    可是即使在这样的情况下,人们还是会在手中留一部分钞票甚至是很大一部分钞票。

    为什么呢?因为他们别无选择,他们把钞票留在手中是为了应付将来可能出现的各种各样的风险,比如生病养老失业等等。

    其次,刺激消费的手段有用吗?

    不得不说,在一定程度上是有用的,比如汽车补贴和家用电器补贴政策,一些原本就打算买家电的人,看到有补贴就提前购买了,另外一些原本没有打算购买的,看到有补贴之后价格下降觉得可能需要就买了。

    可能一部分原因是他们觉得反正钞票留在手中也是越来越贬值的。

    最后,刺激消费的政策是对的吗?

    从经济学的角度来讲,刺激消费是对市场价格的一种扭曲,正常情况下,市场已经不需要更多的家电了,但是刺激消费的政策出来之后,商家发现家电的销量上升了,厂家会因此加大产量,而一段刺激消费的政策停下来,厂家就会大量亏损,因为根本卖不出去。

    同时刺激消费是一种对部分人的补贴,补贴就意味着拿所有的人的钱去帮助一部分人,而且还是强制性的。

    任何时候补贴都是一种不公正的行为。

    打个比方,一个村子里面,村长看到有些穷人家里没有冰箱,就说现在村里每个人交50块钱给那些没有冰箱的人去买冰箱,当然收上来的钱村长肯定还要贪污一部分,生产冰箱的人也要给村长一些好处,这就是补贴政策。

    表面上看那些穷人拿到了冰箱,人们们看不到的是本来有些人想去买洗衣机的,结果因为收走了50块钱就买不成了。生产冰箱的人的利润来自于生产其他东西的人的损失。

  • 2019-09-16-自由能交易么

    微信上看了张是之老师的一篇文章

    https://mp.weixin.qq.com/s/Qt5OvRRokUZ9tnasp1XHZg

    里面的主要内容是问孩子的抚养权能否交易,还有年轻人有没有卖肾的自由。

    下面是我给张是之老师的微信公众号写的留言:

    张老师,我觉得这种交易被禁止是不是有市场因素的原因?就是这种交易容易产生纠纷,比如说你因为生活所迫把孩子卖给我了,那以后孩子长大了想找他亲生爸妈,我是不是应该还给你呢,如果还给你,可是当时我已经付过钱了,如果不还给你,我又侵犯了孩子的自由。

    还有卖肾的问题,比如你因为生活所迫把肾卖给别人了,过段时间你反悔了想要回来,因为付过钱了别人肯定不会还给你,这个时候就容易产生纠纷。

    感觉这两个问题就像吸毒一样,理论上讲,你有吸毒的自由,所以毒品应该合法化,可是一旦你吸毒,上瘾之后你就没有办法反悔了,它是一个不可逆的过程,一旦吸毒,你就不可避免的会一步一步的走向死亡,这对整个社会是损失。

    就像禁止吸烟,本来吸烟是每个人的自由,可是现在政府规定公共场所禁止吸烟,因为吸烟会危害身体健康,对社会造成损失。

    理论上讲,如果政府像禁毒一样全面禁烟,可能带来的医疗费用的降低是大于禁烟的成本的,整个社会是受益的。

    可是禁烟的成本太高,而医疗费用的降低这个收益政府又享受不到,相反禁烟还可能导致政府烟草方面收入的降低,所以政府才不愿意禁烟。

    还有一个终级的问题,人有没有把自己卖身为奴的自由,理论上讲,如果一个人愿意把自己卖身为奴肯定是活不下去了,可是卖身为奴这个行为是没办法反悔,一旦你卖身给我为奴隶,理论上讲你所有的产出都是我的,你已经没有自己的自由了,连你身体的支配权都属于我,所以你没有办法再赎回去,因为你已经没有东西可以交换你的自由了。

    我隐约的感到,是不是不能反悔或者说没办法反悔的东西的东西就不能交易?比如说我能雇佣你去帮我坐牢吗,或者更激进一点,我被判了死刑,我能雇佣你去替我受死么?

  • 2019-06-13-人类知识的上限

    今天我在看一篇介绍杨振宁的文章,里面写了杨振宁对于物理之美的理解。

    我突然想到一个问题,很多高深的数学可能绝大多数人都无法理解,比如费马大定理的证明,虽然英国数学家安德鲁怀尔斯已经花了七年的时间证明了费马大定理,但是据说能够完全理解那个证明的人全世界只有几十个。

    我们绝大多数人,就算你把证明拿到我们面前,我们也看不懂,而且有可能是一辈子也看不懂,因为就像猴子是无法理解微积分一样,我们大多数人根本没有办法理解那些高深的数学,那些顶尖数学家跟我们之间的差别可能比我们跟猴子之间的差别还要大。

    以我自己举例,当年我学量子力学,看到量子力学的那些方程,我弄明白了那些数学该怎么算,但是我没有理解背后的物理含义,对于量子力学的五个基本假设,我到现在还没有搞懂 为什么是这样子,而且可能这辈子都搞不懂了。

    所以我觉得我们人类能够理解的知识会有一个上限,如果一个知识超过了所有人能理解的上限,那么这个知识就是人类不可理解的。

  • 密码保护:2019-06-09-关于虚拟货币

    此内容受密码保护。如需查阅,请在下列字段中输入您的密码。

  • 2019-06-05-运气由什么决定

    看了鬼脚七老师的文章在赚钱中修行,里面说运气由福报决定,然后写怎么积累福报。

    对此,我有点不同看法。

    首先,福报这东西看不见摸不着,没办法量化,当然运气这东西也是一样,看不见摸不着。

    所以运气由福报决定这话就是空对空,你想怎么说都行。

    如果从科学的角度讲无法测量的东西,我们应该认为它不存在,就像历史上存在过的很多概念,以太,燃素一样。

    不过运气和福报又有点不一样,因为运气从科学来讲 应该叫偶然因素,是确实存在的,对此科学认为他只是一个随机的量,无法控制。

    就像你用精密的仪器对一个数值进行多次测量,也会有上下的误差一样,你无法解释为什么会有误差,只能说是随机的。

    当然,你可以用更精密的仪器,但是那只是让误差的值更小而已,没有可能完全消除误差。

    其次,认为运气是由随机因素决定的和认为运气是由福报决定的两种信念会带来不同的后果。

    如果认为运气是由随机因素决定的,那你就什么都做不了,因为宇宙运行的客观规律不以个人意志为转移。

    如果认为运气是由福报决定的,就像鬼脚七老师宣传的一样,那么你就可以对运气进行干涉,通过种种行为来积累福报。

    当然,这种行为在科学看来是愚昧的,因为没有任何证据或者实验能够证明积累福报能够对你的运气有所改善,但是不得不说这种信念对社会甚至对个人都是有好处的。

    这种好处体现在两个方面,第一个方面为了积累福报你肯定做的是好事情,不可能做坏事来积累福报,所以你积累福报的行为对社会是有好处的;第二个方面,在某种程度上,做好事真的可能会带来好运,因为根据六度理论,世界其实是很小的,你无意中做一件小事可能会对你后面人生经历产生巨大的影响。

    所谓好人有好报,其实是有一定的事实根据的。

    举个简单的例子,我一个朋友去公司参加面试,面试成功之后前台小姐要他请客,我朋友很好奇,问为什么,前台小姐说因为那天你面试跟我坐的同一班地铁,我看到你给一个老人让位子了,那天面试的时候,技术总监之前已面试了好多人,说今天时间不够只想面一个候选人,行就行,不行就过一段时间再面,在前台等候的有三个人,我正巧认出你了,就让你进去了,其他两个人根本没有见到技术总监,只是让HR面了一下就打发走了。

    所以你说你是不是应该感谢我。

    再举个反面的例子,之前在一个公司的同事,他说公司网站代码有个漏洞,可以零元下单,我们可以找亲戚下几单,反正老板肯定不知道。

    我说这样不太好吧,然后赶紧偷偷把那漏洞补上了。

    后来那个同事离职了自己开公司,他邀我合伙,我想想就拒绝了,后面他的公司确实黄了。

    所以你无意中的一个好的行为有可能在将来会给你带来一个好的反馈,当然这只是有可能,并不是绝对的。

    综上所述,我认为对运气这个问题应该这么看,在理论上我们应该相信运气是随机量,无法控制,因为这是科学的智慧;在实践上我们应该像相信积累福报有用那样做好事,以待将来有可能收到的正面反馈,当然我们做的时候要知道这只是有可能而已,同时如果是正常人我们做好事的时候会感到一种愉悦感,这是马上对自己就有的好处。

  • 2019-06-04-乔布斯关于乳业谎言的话

    今天我跟朋友说我给孩子订了牛奶,他笑着问我有没有听说过乔布斯那个关于牛奶对大众的好处是个谎言的话。

    我还真没听说过,难道乔布斯说牛奶对大众有害处或者是没有好处?

    因为在我看来,牛奶容易吸收,含有的营养也很丰富,价格也不贵,在生活里是很常见的,如果牛奶对人体有害处或者说没有好处,那真是一个惊世骇俗的说法。

    我就谷歌了一下“乔布斯 牛奶业”,找到了乔布斯的一个关于营销的演讲,上面的原文是这样子的,”The dairy industry tried for 20 years to convince you that milk was good for you. It’s a lie, but they tried anyway.”

    翻译出来应该是“乳品行业用了二十年来说服大众牛奶对人有好处。尽管这是个谎言,但他们还是试过。”

    然后我就去谷歌了一下“牛奶业 营销”,找到了一篇文章介绍牛奶叶营销的事情。

    里面写道二战结束之后,因为资源紧缺美国政府就大力宣传牛奶对人体有好处,因为牛奶是比较便宜易得的优质蛋白质来源。

    政府宣传的膳食结构指南里面牛奶这一项是单独列出来的,认为是人体必需的六种食物来源之一。

    可是等到后面经济恢复了,人们的选择更多了,人们对牛奶的消费就减少了,因为牛奶里面的成分不是不可替代的。

    这个时候美国奶制品营销的组织就开始继续把之前的事情拿出来宣传,美国政府甚至搞出了一个饮食结构金字塔,把牛奶放在第二层,就是让人觉得牛奶是必需品一样。

    其实很多人都有乳糖不耐症,是不太适合喝牛奶的,喝了就会拉肚子。

    所以乔布斯那句话应该说才比较符合事实,“奶制品行业用了20年来说服牛奶对你是必需的,这是一个谎言。”

    遗憾的是乔布斯说的是牛奶对你有好处,这是一个谎言。

    乔布斯这样说很容易让人认为牛奶对人没有好处或者有坏处,后来乔布斯又成了教主,这样的观点就广为流传了。

    从营养学的角度来讲,牛奶当然不是必不可少的,但是牛奶确实是一种廉价易得容易吸收的蛋白质和钙的来源,对人是没有坏处的,当然如果你有乳糖不耐症就不要喝了。

  • 2019-06-02-不执着于象棋

    今天,我跟一个好朋友下了两盘象棋,都输了,但是我很开心。

    因为我发现我不执着于象棋了,或者说我不执着于象棋的输赢。

    小学的时候,曾经有一段时间我很痴迷于象棋,我会研究棋谱,甚至会自己跟自己下。

    我会找每个人下棋。

    但是那个时候我会执着于输赢,赢了很开心,输了很难过,会想办法再跟别人下,一定要赢回来。

    有的时候因为对手太强大,发现自己根本没有可能赢,我就会非常生气, 甚至会乱发脾气。

    后来因为要上高中大学,我很长一段时间都没有下棋。

    再等到后面,我电脑上装了一个象棋2000的程序,我发现无论是我还是我认识的所有人都下不赢它,我就对象棋彻底没兴趣,或者说是绝望了。

    时间再往后,就是前几年,智能手机上可以下象棋,我又下载了一个中国象棋的app,但是我很少跟人下,我喜欢闯关。

    可能是因为我太执着输赢,跟人下棋,赢了我没多开心,输了觉得很难受,所以不愿意跟人下棋,只喜欢跟电脑下闯关的,练练脑子打发时间。

    然后就到了今天,我朋友说好久没下象棋了,要不来几盘,我也有点心动了。

    我朋友说几年前他跟我下过一次,输给我了,所以印象深刻,我自己是没什么印象了。

    但是看到他想找回场子,我肯定要给他个机会。

    然后他赢了两盘。

    我妈来叫我去吃饭,我就跟我妈走了。

    在去我妈那儿的路上,我发现自己一点都不难受,我就感觉很开心,因为我终于放下了对象棋输赢的执着。

    这种开心比赢了象棋更开心,因为从今以后象棋又会重新成为我的乐趣,而且再也不能给我带来负面的感受,就是赢了我很开心,输了我也很开心。

    按照佛家的说法,成佛就是放下对于一切的执着,这个一切包括成佛本身,当然成佛本身是要最后放下的。

    佛法就像洗去污垢的肥皂,佛法的目的是洗去烦恼,到最后污垢和肥皂都没有了,也就是烦恼和佛法都没有了,你的目的就达到了。

    凡人都陷入污垢里面无法自拔,而圣者又容易陷入肥皂里面无法自拔,从终极目的来看,污垢与肥皂并没有本质上的区别。

    我想这就是宗萨蒋扬钦哲仁波切所说的,佛法是一场诡计的意思吧。

    再说回象棋,放下对象棋的执着对我是一个进步,我应该属保持警醒,逐步的放下对所有事物的执着。

    因为成佛就是放下对一切执着,到最后连对放下的执着也要放下。

  • 2019-05-31-政府补贴问题

    刚才看了公众号奥派经济学的文章,忘掉京东方,里面反对京东方补贴,还是奥派经济学一贯的主张,补贴就是扭曲了市场,赚不到钱的企业通过政治手段拿钱,全社会因此蒙受了看不见的损失。

    我一贯支持奥派经济学的观点,但是在这个问题上我有点不同看法。

    物理上有一个阈值的概念,没有达到阈值反应就不能发生。

    就像曼哈顿工程一样,如果不是美国政府投入,可能人类就没有原子弹这种武器了。

    相同的道理,像芯片这种需要很高投入才能得到的技术,如果没有政府的这种没有效率的投资,可能我们就得不到这种技术了。

    其实中国的芯片技术军用是有的,因为军队用所以不计成本。

    政府的投入可以解决从无到有的问题,而市场能解决生产效率的问题。

    张老师也说了市场造不出核弹,因为核弹没用。

    但是美国人造出了核弹,我们必须造出核弹。

    就像芯片,理论上市场可以提供芯片,我们自己没必要补贴投资研发芯片,但是一旦像现在这种情况发生我们就很被动了。

    市场唯一做不到的就是维持市场本身的存在,总会有暴力试图破坏市场,所以一个国家必须有一个终极暴力来维持市场的持续存在。

    问题是这个终极暴力为了本身的利益可能用种种管制,调控,补贴,准入制度来破坏市场。

    这个问题是无解的。

  • 2019-05-30-胡思乱想

    听到儿子喊爸爸,我回头看了儿子一眼,儿子笑嘻嘻的说,“长大以后我要在我的世界里面做一个东西。”我下意识地回答,“什么东西?”

    儿子开始滔滔不绝地描述他想要做的那个东西,我却开始走神了。我恍恍惚惚有种不真实的感觉,我的儿子已经这么大啦?这会不会是一场梦,我醒来之后发现自己还在大学宿舍里面?

    我从儿子身上看到了过去和将来,看到了一个小宝宝和一个老人,看到了自己,看到了整个宇宙。整个宇宙所有的一切都能和我儿子联系到一起,因为所有的一切都源自140亿年前的那次爆炸。

    宇宙间所有一切的物质和现象,本质上都是能量弦的振动。普朗克长度量级的能量弦不同的方式和频率组合成了基本粒子,基本粒子组成原子,原子组成了大千世界。所以归根到底我们是能量聚合体,世间的一切现象也只是能量的聚合与消散。

    想到这里,我突然有一种莫名的安全感。我和儿子和整个宇宙都是一体的,本质上只是能量而已。

    这个时候儿子又问我,“我说这个东西怎么样,厉害吧?”我连忙看向儿子,“厉害,那是宇宙中最厉害的。”

  • 2019-05-28-说线做泪

    今天中午老大让我给网页服务器装一个地理位置的插件,原本以为半个小时就能搞定,结果我花了整整一个下午五个多小时才搞定。

    搞定之后,我把所有的步骤都很详细的写在报告里面。

    写完报告之后,我就很感慨,我算是理解了什么叫做说线做泪。

    如果我事先就有我最后写的那分报告,整个安装时间十五分钟就能搞定。

    写那个报告之前的十分钟,我还在怀疑能不能搞定,因为已经搞了好几个小时,踩了无数的坑,我都搞得有点怀疑人生了。

    简单来说网页服务器的版本,插件的版本,操作系统依赖关系,手动编译网页服务器用的参数,网页服务器的配置有一个不对最后就没有办法安装成功。

    其中最后一步的坑最大,我把插件放在网页服务器的默认插件目录,然后在配置文件里面写上加载xx插件,结果死活打不开,google搜索也没有任何线索,我想起以前有一次另外一个完全不相关的软件打不开的情况,我用类似的方法处理,居然搞定了。

    想想这件事,我觉得跟人生里面要做的大部分事情都很像,做的时候有很多不确定的因素,你甚至不知道最后目标能不能达到。

    就像竖鸡蛋的例子一样,哥白尼没说出来之前,谁都不知道怎么不依靠任何东西把鸡蛋竖起来,哥白尼说出答案之后这个问题就不值一文了。

    这件事情对我有什么启发呢?

    我想第一点就是,不要把任何一件事情看的很简单,可能一句“根据提示安装依赖关系”,你就要忙活好几个小时。

    第二点就是不要轻易放弃,假如我在最后安装成功之前,不做最后一次尝试,那可能结果就是那个插件安装不成功。

  • 2019-05-27-关于余秋雨的看法

    看了鬼脚七写的谈谈中国文化 的文章,看着我一直觉得很别扭,总感觉不像是七哥写的,看到文章末尾才知道这是鬼脚七看余秋雨的中国文化课程写的。

    这下我知道为什么别扭了。

    以前里面大学的时候我就看过余秋雨的文化苦旅,就感觉很装很别扭,后来看了一本书,叫做石破天惊逗秋雨,里面写的就是余秋雨的书里面的种种常识性的错误。

    本来犯错也没什么,但是余秋雨的那个时候的回应就很令人恶心,种种狡辩说自己没犯错。

    然后我对余秋雨的印象就差了很多,感觉就是那种最传统的酸腐文人,而且还是那种卑鄙小人。

    我的印象里面文化苦旅里面的都是一些煽情的文字,没有什么内容,全是一些感情的描写,华丽的辞藻堆上去。

    如果说人类文字有的是有用的,有的是没有用的,那么文化苦旅这种文字就是最没有用的。

    今天看七哥写的那个余秋雨的课程内容也是给我这种感觉,看着就觉得空洞没有内涵。


    写完上面的文字,我安静了一会儿,回头把上面的文字又看了一遍,突然觉得自己很可笑。

    如果余秋雨的文字没有用,那我这篇文字又有什么用呢?

    我在上面对余秋雨的指责全部可以用到自己的这篇文章上面。

    而且我怎么知道我对余秋雨的那些感觉是不是一种偏见呢?

    可能因为我的思维里面无法容忍错误,更无法容忍拒绝承认错误,尤其是像余秋雨这种号称文化大师的名人,所以看了石破天惊逗秋雨之后,我一下子把余秋雨贬到很低的印象里面。

    在我的印象里面,中国文化大师应该是一个君子,所谓君子坦荡荡,错了就是错了,麻利地认错改正才是君子所为。

    因为余秋雨的行为与我对君子的预期不符合,所以我就把他贬低成一个小人,我这样的行为是不是君子所为呢?

    还有一点,我对余秋雨的印象是十年前的,我怎么知道这十年里面他有没有改正过来呢?

    我没有看他最新的中国文化的课程,说不定他在那个课程里面已经改正了以前的错误呢?

    所谓人非圣贤,孰能无过?

    幸好我写的这篇文章梳理自己的想法,否则的话,我对余秋雨的偏见还会一直持续下去。

    就像鬼脚七说的,写作真是一种修行。

  • 2019-05-24-先后关系与因果关系

    刚才看了一篇关于走路太多是否会影响膝关节的文章,里面的结论是走路与膝关节损伤没有必然的因果关系。

    文章结尾有一段话,我觉得很要命。

    上面说膝关节炎是很常见的病,走不走路都可能得膝关节炎。

    我猜想认为走路太多会损伤膝盖这种说法就是这么来的,有些人走路锻炼一段时间之后发现得了膝关节炎,然后就说自己因为走路太多,然后得了膝关节炎。

    他们不知道先后发生的两件事情不一定是因果关系,可能两件事完全没有关系。

    就像古代求雨,那些老百姓献上牲口去庙里面请人做法然后天就下雨了。

    他们以为因为献祭,所以下雨。

    其实献祭和下雨这两件事就没有因果关系,只是时间上有先后关系,说通俗一点就是凑巧。

    有人会问为什么会每次都凑巧呢?

    首先,他们去献祭的时间一般是雨季快来临的时候或者就是雨季,所以献祭之后下雨的概率本身就很高。

    其次,举行献祭的人肯定会宣传献祭之后成功下雨的事,没有人会宣传献祭之后一直没下雨。

    即使真没有下雨,那些献祭的法师或者官员也会找出种种借口,比如祭品有问题,或者献祭的老百姓触怒了上天。

    总之,这些无法证实的原因全靠他们一张嘴。

    还有一点,普通人对比较戏剧性的事情印象比较深刻,同时人类有遗忘痛苦的倾向。

    还是以求雨来举例子,如果和尚做法第二天或者是当天就下了雨,老百姓就会觉得“哇,这个太厉害了,”几十年都不会忘记。

    相反,如果献祭之后几天甚至几个月之后才下雨,甚至根本就没下雨,那么老百姓就不会有印象。

    再加上在古代如果求雨失败了,那么老百姓可能会生活很惨,可能会遇上灾荒,甚至会饿死人,由于人类遗忘痛苦的倾向,人们会倾向于忘记这件事。

    这样一来,人们有印象的都是那些求雨成功的案例。

    多数人一生都没有理解先后关系与因果关系的差别。

    下雨还是比较简单的案例,毕竟有云就有可能下雨。

    我举一个更复杂的例子,我们身体本身。

    比如你得感冒了,然后你吃了板蓝根,感冒就好了。

    而且这板蓝根非常灵,每次感冒都是这样。

    然后你就很容易得出结论,因为吃了板蓝根,所以感冒好了。

    实际上你吃板蓝根跟你感冒好没有因果关系,只有先后关系。

    有人会问那为什么每次都这样呢?因为感冒是一种自愈性疾病,就是它自己就会好,人体的免疫系统自己就会把病毒或者细菌干掉,跟你吃不吃板蓝根没关系,你就算是每天都喝一大杯水,你的感冒也会好。

    正因为感冒的这个特点,各种千奇百怪的治感冒的偏方都很有效,吃辣椒,喝醋,蒙头盖被子出汗,按摩背心,热敷,冰敷都有不错的效果。

    只要你的治疗手段不对免疫系统造成太大的伤害,这些手段最后都会生效。

    因为生效的不是这些偏方,而是你的免疫系统。

    因为科学尚未普及,所以大部分人根本不知道人体有免疫系统可以抵抗大部分人类已知的疾病。

    就是这些自愈性疾病给了一些千奇百怪的药物和偏方操作空间,几乎所有的中药都是靠这些自愈性疾病才存活下来。

    唉,科学尚未普及!

  • 2019-05-20-关于南怀瑾的看法

    最近我在看南怀瑾写的金刚经说什么,没看几页就看到一些神神叨叨的东西。

    虽然南怀瑾的名气很大号称国学大师,但是因为这些神神叨叨的东西我一直很不喜欢,感觉他在胡说八道。

    现在我仔细想想,也许所谓的事实对每个人来说是不一样的,也许南怀瑾并没有故意骗人,他就是相信那些不科学的事情能够发生。

    在我的思想里面不科学的事情是不可能发生的,但是在他的思想里面没有这个限制。

    如果从最彻底的佛学角度来讲,凡所有相,皆是虚妄,我们所看到的事实也是虚妄。

    从这个逻辑推演下去,科学也是虚妄。

    想到这里,我想起一句话,宁执有见如须弥山,不执空见如芥子许。

    这里认为科学是虚妄算不算空见呢?

    佛教三法印:诸行无常,诸法无我,涅盘寂静。

    这里的诸行无常,诸法无我不是一切都是虚妄的意思,而是一切事物都是无常变幻,因缘和合的产物,没有不变的本质。

    如果从这个角度去理解,佛教并没有否定科学,没有否定物质的本质或者物质现象的发生规律。

    继续推演下去,那么南怀瑾说的那些神神叨叨的东西就是假的,要么是未经验证,要么是以讹传讹。

    当然,这不影响我继续看南怀瑾的书,多看看别人的理解没有坏处,只要仔细分别哪些有道理哪些是鬼扯就行了。

  • 2019-05-18-权责对等

    刚才看到一篇文章《男女平权,纯属放屁》

    里面提到一个概念,权责对等,就是有多大权利就要承担多大责任。

    文章里面说的是如果男的责任是赚钱养家保护妻儿,那么他的权利就是在家里面得到尊重;对应的如果女的责任是在家里面相夫教子,那么她的权利就是不用为家庭生计担心,不用担心外人的侵犯。

    当然,这种传统的男主外女主内的社会模型到现代社会已经有点不成立了。

    因为现在生活压力太大,女性也不得不出来工作养家糊口,甚至有不少女性比男的赚的还多。

    这样一来,很多女性是不愿意结婚的,何必要嫁一个老爷回来养着呢?

    关于权责对等,我还有更多的想法。

    权责对等用在一个人身上就是要对自己的事情负责,自己做的事情要自己承担责任,不能甩锅。

    权责对等用在家庭上面,就是上面说的不可能让一个人既主外又主内,如果两个人都要赚钱养家的话,那家里的事情也应该两个人分担。

    权责对等用在国家上面,那就是一个大话题了,简而言之,统治阶级收多少钱就应该办多少事,像天朝这样光收钱不办事儿,赋税全球第二高,福利恨不得跟非洲国家看齐,只能怪我们草民没办法反抗。

    从草民的角度来看最有利的权责对等模型就是统治阶级尽可能的小,管的事儿尽可能的少,除了国防外交和公安这三个必备的部门,以及维持三个部门运转所需要的民政部和财政部以外,其他的部门都没有必要存在。

    可惜这个世界不会对草民这么友好,任何一个统治阶级都不会甘心只是维持手上的权利,他们一定会想办法扩大手中的权利把手伸得更长,干涉草民生活的方方面面就是他们扩大权利的方法。

    糟糕的是,大部分草民都意识不到统治阶级伸长爪子的坏处,他们只看到统治阶级提供了更多的福利,他们没有想过他们要付出的代价。

    他们误以为有免费的早餐,误以为不需要承担代价。

    所以权责对等真是一个好概念,值得普及!

  • 2019-05-17-古不如今

    我今天凌晨才到家,在路上的时间有将近八个小时,早上7.30家里出发坐715公交车转13号线转10号线去虹桥火车站赶9.40去杭州东的D3107动车,11.25到杭州东,然后1号线到西兴站转一个摩的,12.20到了东忠科技园;回来的路上,21.00从鑫耀假日酒店打的21.20到杭州东站,打的路上订了22.00开往虹桥的G2366高铁,进站之后发现G2366晚点一个小时,乘务员让我上了21.40出发的D2288,22.35到了虹桥火车站,然后赶上了最晚一班22.48分开的地铁2号线,到龙阳路转23.43分的798公交车回到家。

    到家之后我的第一个感受是累,第二个想法是这真是一个奇迹,一个现代社会才可能发生的奇迹,一个普通人一天之内来回800里(412公里),早上从上海出发去杭州工作,晚上再回上海睡觉。

    公交,地铁,高铁,滴滴每一项都是一个复杂的系统,无数人的协作才能使这些复杂系统高效又低成本的运行。

    如果是在古代,一天赶800里路,只能骑上传说中的千里马,或者驿站不停地换马,跑完800里路,半条命都没了; 即使是现代,大多数国家都没有高铁,如果要来回800里,只能自己开车,即使全程高速也要开四个多小时。

    如果我把今天一天的经历告诉一个古人,他一定认为这是天方夜谭,公交车,地铁,高铁和滴滴背后的科技对他来说都是不可能理解的。

    古代不如现代的地方不仅仅限于交通方面,我们日常的衣食住行,娱乐,学习所有方面都完爆古人。

    我能想到古人生活优于我们的地方就是古代环境污染比较少,可是对于大多数古人来说,能吃饱饭就不错了,那有什么心情去欣赏美景,而且虽然古人没有重工业的污染,但是古人的卫生条件并不好,没有下水系统,没有卫生知识,没有靠谱的医学知识,这些导致古代平均寿命只有30多岁。

    汉族作为一个农耕民族,有很重的崇古思想,因为在传统的农耕社会里面,自然环境没有什么变化,老人的经验就显得很宝贵,年轻人就倾向于认为以前的经验就是对的,加上老人普遍比较怀旧,总认为过去比较好,这样整个社会都认为古代比较好,推理下去就认为三代是黄金时代,后面越来越不如前面。

    这种崇古思想在古代没有什么问题,甚至可能有好处,至少对皇权是一种制约。

    但是到了现代社会,一个人如果还抱有崇古思想,那只能说是愚昧了。

    现代社会是从古代社会进化而来的,我们运用科学技术来改造社会的方方面面,因此几乎没有什么方面是不如古代的。

    我在文章第一段里面列了那么多准确的时间不是因为我的记性好,而是因为这些时间都可以在支付宝里面看到。

    至于未来,我相信会比今天更好。

    随着物联网技术的发展,在未来,我们所有人的方方面面的数据都会连接到一个系统上面,然后我们所有人的生活就像一个超级巨型的虚拟生活游戏系统,只不过我们的衣食住行的真实需求都可以由这个系统来解决和满足,相比之下,国家(如果还存在的话)可能会成为一个无足轻重的角色。

    不过仔细想想,这样一个社会可能会很可怕。

    因为如果那个系统作恶,个人是完全无力反抗的。

    但是对于个人而言,将来每个人的生活水平的方方面面肯定会超过现在的人。