0%

面向对象

  1. 例子
    1
    2
    3
    4
    class Person {
    public String name;
    public int age;
    }

    一个class可以包含多个字段(field),字段用来描述一个类的特征。上面的Person类,我们定义了两个字段,一个是String类型的字段,命名为name,一个是int类型的字段,命名为age。因此,通过class,把一组数据汇集到一个对象上,实现了数据封装。

    字段就是C++中的成员属性

  2. 创建实例: Person ming = new Person();
  3. 小结:
    • 在OOP中,class和instance是“模版”和“实例”的关系;
    • 类就是对象的模板(蓝图)
    • 定义class就是定义了一种数据类型,对应的instance是这种数据类型的实例;
    • class定义的field,在每个instance都会拥有各自的field,且互不干扰;
    • 通过new操作符创建新的instance,然后用变量指向它,即可通过变量来引用这个instance;
  • 访问实例字段的方法是变量名.字段名;
  1. 类和对象的关系

    • 类是一个抽象的概念,仅仅是模板,比如说:演员、总统
    • 对象是一个你能够看得到、摸得着的具体实体
    • 类的定义者和类的使用者是不一样的。越往下越具体,越往上越抽象。
  2. 基本步骤

    1. 发现类
    2. 找出属性(名词)
    3. 找出行为(动词)
    4. 数据抽象:是数据和处理方法的结合
  3. 定义类

    1. 代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public class Actor {
      public String name;
      public char sex;
      public String job;
      public int age;

      public void eat(){
      ……
      }
      }
  4. 类图

    1. 直观、容易理解
    2. 参考工具:
      • StarUML
      • Astah
      • +号代表public,-号代表private
      • 属性名在前,后面跟冒号和类型名
      • 方法名在前,后面跟冒号和返回值类型
      • 如果有参数,参数的类型写法同上
      • Astah
  5. 实例化

    1
    2
    3
    Role role1 = new Role(); // 在堆空间里面分配了一个空间,把空间的地址赋给了role1,用哈希码来表示在虚拟机里面的内存地址
    Role role2; // 声明了一个Role类型的变量,叫role2(可以把Role看成自己定义的数据类型),但是还没有空间
    role2 = new Role(); // role2 一定要初始化,对象在运行时,一定要分配空间
  6. 方法

    把field从public改成private,外部代码不能访问这些field,以我们需要使用方法(method)来让外部代码可以间接修改field:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class Main {
    public static void main(String[] args) {
    Person ming = new Person();
    ming.setName("Xiao Ming"); // 设置name
    ming.setAge(12); // 设置age
    System.out.println(ming.getName() + ", " + ming.getAge());
    }
    }

    class Person {
    private String name;
    private int age;

    public String getName() {
    return this.name;
    }

    public void setName(String name) {
    this.name = name;
    }

    public int getAge() {
    return this.age;
    }

    public void setAge(int age) {
    if (age < 0 || age > 100) {
    throw new IllegalArgumentException("invalid age value");
    }
    this.age = age;
    }
    }

    虽然外部代码不能直接修改private字段,但是,外部代码可以调用方法setName()和setAge()来间接修改private字段。在方法内部,我们就有机会检查参数对不对。比如,setAge()就会检查传入的参数,参数超出了范围,直接报错。这样,外部代码就没有任何机会把age设置成不合理的值。

    同样,外部代码不能直接读取private字段,但可以通过getName()和getAge()间接获取private字段的值。

    所以,一个类通过定义方法,就可以给外部代码暴露一些操作的接口,同时,内部自己保证逻辑一致性。

    调用方法的语法是实例变量.方法名(参数);。一个方法调用就是一个语句,所以不要忘了在末尾加;。例如:ming.setName(“Xiao Ming”);。

    1. 定义方法

      1
      2
      3
      4
      修饰符 方法返回类型 方法名(方法参数列表) {
      若干方法语句;
      return 方法返回值;
      }
    2. private方法

      有public方法,自然就有private方法。和private字段一样,private方法不允许外部调用,定义private方法的理由是内部方法是可以调用private方法的。

  7. this变量

    在方法内部,可以使用一个隐含的变量this,它始终指向当前实例。因此,通过this.field就可以访问当前实例的字段。如果没有命名冲突,可以省略this,但是如果有局部变量和字段重名,那么局部变量优先级更高,就必须加上this

  8. 方法参数

    方法可以包含0个或任意个参数。方法参数用于接收传递给方法的变量值。调用方法时,必须严格按照参数的定义一一传递。(同C++)

  9. 可变参数

    可变参数用 类型... 定义,可变参数相当于数组类型:

    1
    2
    3
    4
    5
    6
    7
    class Group {
    private String[] names;

    public void setNames(String... names) {
    this.names = names;
    }
    }

    上面的setNames()就定义了一个可变参数。调用时,可以这么写:

    1
    2
    3
    4
    5
    Group g = new Group();
    g.setNames("Xiao Ming", "Xiao Hong", "Xiao Jun"); // 传入3个String
    g.setNames("Xiao Ming", "Xiao Hong"); // 传入2个String
    g.setNames("Xiao Ming"); // 传入1个String
    g.setNames(); // 传入0个String

    完全可以把可变参数改写为String[]类型:

    1
    2
    3
    4
    5
    6
    7
    class Group {
    private String[] names;

    public void setNames(String[] names) {
    this.names = names;
    }
    }

    但是,调用方需要自己先构造String[],比较麻烦。例如:

    1
    2
    Group g = new Group();
    g.setNames(new String[] {"Xiao Ming", "Xiao Hong", "Xiao Jun"}); // 传入1个String[]

    另一个问题是,调用方可以传入null:

    1
    2
    Group g = new Group();
    g.setNames(null);

    而可变参数可以保证无法传入null,因为传入0个参数时,接收到的实际值是一个空数组而不是null。

  10. 参数绑定: 传参的问题

    基本类型参数的传递,是调用方值的复制。双方各自的后续修改,互不影响。

    引用类型的传递:将地址进行复制,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Main {
    public static void main(String[] args) {
    Person p = new Person();
    String[] fullname = new String[] { "Homer", "Simpson" };
    p.setName(fullname); // 传入fullname数组
    System.out.println(p.getName()); // "Homer Simpson"
    fullname[0] = "Bart"; // fullname数组的第一个元素修改为"Bart"
    System.out.println(p.getName()); // "Bart Simpson"
    }
    }

    class Person {
    private String[] name;

    public String getName() {
    return this.name[0] + " " + this.name[1];
    }

    public void setName(String[] name) {
    this.name = name;
    }
    }

    Note: 因为传入的是字符串数组,因此将 fullname 数组的地址复制,传给了p.name,因此 p.namefullname 指向的是同一个字符串数组,所以这两个同时变化,但是变化的时候还是遵循引用的原则:Homer仍然存在,只是无法通过fullname[0] 进行访问罢了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Main {
    public static void main(String[] args) {
    Person p = new Person();
    String bob = "Bob";
    p.setName(bob); // 传入bob变量
    System.out.println(p.getName()); // "Bob"
    bob = "Alice"; // bob改名为Alice
    System.out.println(p.getName()); // "Bob"
    }
    }

    class Person {
    private String name;

    public String getName() {
    return this.name;
    }

    public void setName(String name) {
    this.name = name;
    }
    }

    Note: 和上一个传入引用参数一样,复制的是地址,传入之后 bobp.name 指向了同一块内存,只不过 bob 改变之后会重新指向新的内存,所以这两个变量指向的内存就不一样了。

    注意:字符串和数组都是引用类型。

  11. 构造方法

    1. 由于构造方法是如此特殊,所以构造方法的名称就是类名。构造方法的参数没有限制,在方法内部,也可以编写任意语句。但是,和普通方法相比,构造方法没有返回值(也没有void),调用构造方法,必须用new操作符。(具体意义和C++一样)
    2. 没有在构造方法中初始化字段时,引用类型的字段默认是null,数值类型的字段用默认值,int类型默认值是0,布尔类型默认值是false:
    3. 可以对字段直接进行初始化:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Person {
    private String name = "Unamed";
    private int age = 10;

    public Person(String name, int age) {
    this.name = name;
    this.age = age;
    }
    }

    构造方法的代码由于后运行,最终由构造方法的代码确定,即便已经直接将字段初始化了。

  12. 方法重载

    同C++

  13. 继承

    Student类包含了Person类已有的字段和方法,只是多出了一个score字段和相应的getScore()、setScore()方法。

    继承是面向对象编程中非常强大的一种机制,它首先可以复用代码。当我们让Student从Person继承时,Student就获得了Person的所有功能,我们只需要为Student编写新增的功能。

    1. 实现

      Java使用extends关键字来实现继承:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      class Person {
      private String name;
      private int age;

      public String getName() {...}
      public void setName(String name) {...}
      public int getAge() {...}
      public void setAge(int age) {...}
      }

      class Student extends Person {
      // 不要重复name和age字段/方法,
      // 只需要定义新增score字段/方法:
      private int score;

      public int getScore() { … }
      public void setScore(int score) { … }
      }

      通过继承,Student只需要编写额外的功能,不再需要重复代码。

      Note: 子类自动获得了父类的所有字段,严禁定义与父类重名的字段!

      在OOP的术语中,我们把Person称为超类(super class)父类(parent class)基类(base class),把Student称为子类(subclass)扩展类(extended class)

    2. 继承树

      在Java中,没有明确写extends的类,编译器会自动加上extends Object。所以,任何类,除了Object,都会继承自某个类。

      Java只允许一个class继承自一个类,因此,一个类有且仅有一个父类。只有Object特殊,它没有父类。

      例如:

    3. protected

      继承有个特点,就是子类无法访问父类的private字段或者private方法。

      为了让子类可以访问父类的字段,我们需要把private改为protected。用protected修饰的字段可以被子类访问: 因此,protected关键字可以把字段和方法的访问权限控制在继承树内部,一个protected字段和方法可以被其子类,以及子类的子类所访问。

    4. super

      super关键字表示父类(超类)。子类引用父类的字段时,可以用super.fieldName。例如:

      1
      2
      3
      4
      5
      class Student extends Person {
      public String hello() {
      return "Hello, " + super.name;
      }
      }

      实际上,这里使用super.name,或者this.name,或者name,效果都是一样的。编译器会自动定位到父类的name字段。

      但是,在某些时候,就必须使用super。我们来看一个例子:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      public class Main {
      public static void main(String[] args) {
      Student s = new Student("Xiao Ming", 12, 89);
      }
      }

      class Person {
      protected String name;
      protected int age;

      public Person(String name, int age) {
      this.name = name;
      this.age = age;
      }
      }

      class Student extends Person {
      protected int score;

      public Student(String name, int age, int score) {
      this.score = score;
      }
      }

      运行上面的代码,会得到一个 编译错误,大意是在Student的构造方法中,无法调用Person的构造方法。

      这是因为在Java中,任何class的构造方法,**第一行语句必须是调用父类的构造方法。如果没有明确地调用父类的构造方法,编译器会帮我们自动加一句super();**,所以,Student类的构造方法实际上是这样:

      1
      2
      3
      4
      5
      6
      7
      8
      class Student extends Person {
      protected int score;

      public Student(String name, int age, int score) {
      super(); // 自动调用父类的构造方法
      this.score = score;
      }
      }

      但是,Person类并没有无参数的构造方法,因此,编译失败。

      解决方法是调用Person类存在的某个构造方法。例如:

      1
      2
      3
      4
      5
      6
      7
      8
      class Student extends Person {
      protected int score;

      public Student(String name, int age, int score) {
      super(name, age); // 调用父类的构造方法Person(String, int)
      this.score = score;
      }
      }

      如果父类没有默认的构造方法,子类就必须显式调用super()并给出参数以便让编译器定位到父类的一个合适的构造方法。

      这里还顺带引出了另一个问题:即子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。

    5. 阻止继承

      只要某个class没有final修饰符,那么任何类都可以从该class继承。

      从Java 15开始,允许使用sealed修饰class,并通过permits明确写出能够从该class继承的子类名称。

      1
      2
      3
      public sealed class Shape permits Rect, Circle, Triangle {
      ...
      }

      上述Shape类就是一个sealed类,它只允许指定的3个类(Rect, Circle, Triangle)继承它,否则就会报错。

      这种sealed类主要用于一些框架,防止继承被滥用。sealed类在Java 15中目前是预览状态,要启用它,必须使用参数–enable-preview和–source 15。

    6. 向上转型:一个子类类型安全地变为父类类型的赋值

      向上转型实际上是把一个子类型安全地变为更加抽象的父类型

      1
      2
      3
      4
      Student s = new Student();
      Person p = s; // upcasting, ok
      Object o1 = p; // upcasting, ok
      Object o2 = s; // upcasting, ok

      p也只能使用 Person类中有的字段和方法,不能使用 Student 添加的字段和方法

    7. 向下转型:把一个父类类型强制转型为子类类型

      不能把父类变为子类,因为子类功能比父类多,多的功能无法凭空变出来。

      1. instanceof实际上判断一个变量所指向的实例是否是指定类型,或者这个类型的子类。如果一个引用变量为null,那么对任何instanceof的判断都为false
      1
      2
      3
      4
      5
      Person p = new Student();
      if (p instanceof Student) {
      // 只有判断成功才会向下转型:
      Student s = (Student) p; // 一定会成功
      }

      从Java 14开始,判断instanceof后,可以直接转型为指定变量,避免再次强制转型。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      public class Main {
      public static void main(String[] args) {
      Object obj = "hello";
      if (obj instanceof String s) {
      // 可以直接使用变量s:
      System.out.println(s.toUpperCase());
      }
      }
      }

      等价于

      1
      2
      3
      4
      5
      Object obj = "hello";
      if (obj instanceof String) {
      String s = (String) obj;
      System.out.println(s.toUpperCase());
      }
    8. 组合和继承

      1
      2
      3
      4
      5
      class Book {
      protected String name;
      public String getName() {...}
      public void setName(String name) {...}
      }

      这个Book类也有name字段,那么,我们能不能让Student继承自Book呢?不可以

      从逻辑上讲,这是不合理的,Student不应该从Book继承,而应该从Person继承。

      究其原因,是因为Student是Person的一种,它们是is关系,而Student并不是Book。实际上Student和Book的关系是has关系。
      具有has关系不应该使用继承,而是使用组合,即Student可以持有一个Book实例:

      1
      2
      3
      4
      class Student extends Person {
      protected Book book;
      protected int score;
      }

      继承是is关系,组合是has关系。

  14. 多态

    在继承关系中,子类如果定义了一个与父类方法签名完全相同的方法,被称为覆写(Override)。

  • 常用命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @attention           注意
    @author 作者
    @bug 缺陷,链接到所有缺陷汇总的缺陷列表
    @brief 简要注释
    @code 代码块开始,与“endcode”成对使用
    @endcode 代码块结束,与“code”成对使用
    @details 详细注释
    @date 日期
    @file < 文件名> 文件参考,用在文件注释中
    @param 参数,用在函数注释中
    @return 返回,用在函数注释中
    @todo TODO,链接到所有TODO 汇总的TODO 列表
    @version 版本
    @warning 警告
  • vscode 用户自定义片段

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    "cppDocuNote": {
    "prefix": "docuNote",
    "body": [
    "/**",
    " * $1",
    " * @Params: $2",
    " * @Return: $3",
    " */$0",
    ],
    "description": "Documentation notes"
    }

  1. 子序列

    子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,”ace”是”abcde”的一个子序列,而”aec”不是)。

  2. 整除

    若整数b除以非零整数a,商为整数,且余数为零, 我们就说b能被a整除(或说a能整除b),b为被除数,a为除数,即a|b(“|”是整除符号),读作“a整除b”或“b能被a整除”。a叫做b的约数(或因数),b叫做a的倍数。整除属于除尽的一种特殊情况。

  1. 程序设计返回结果

    1. Accepted: 通过
    2. Wrong Answer: 答案错误
    3. Runtime Error: 表示程序因为非法访问或未处理异常而结束。
    4. Memory Limit Exceeded: 表示程序因为使用的内存超过规定的内存限制。
    5. Presentation Error: 表示虽然程序输出的答案是对的,但是换行或空格等不符合输出格式要求
    6. Time Limit Exceed: 超时
    7. Output Limit Exceeded: 表示程序输出了过多的内容。
    8. Compile Error: 表示所提交的源代码没能通过编译,不符合语法规定。
    9. System Error, Validator Error: 表示系统发生错误无法正常判题。
    10. Segmentation Fault: 段错误:访问的内存超过了系统所给这个程序的内存空间
  2. 状态

    例子:找最短路径的问题中,状态仅仅是目前所在位置的坐标。

    当状态更加复杂是,就需要封装成一个类来表示转态了。

  3. 状态转移、转移的方式

    例子:同1中的例子,转移的方式为4个方向移动

    又如:八连通($P_{32}$),8个方向共对应了8种状态转移。

  4. next_permutation 函数使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int main()
    {
    int a[4] = {1, 5, 4, 3}, i;
    sort(a, a + 4); //在使用全排列函数之前,一定要先将数组中的数排序哦(不排序好像也可以)
    do {
    for (i = 0; i < 4; i++) cout << a[i] << " ";
    cout << endl;
    } while (next_permutation(a, a + 3));

    for (i = 0; i < 4; i++) cout << a[i] << " ";
    return 0;
    }

    使用 next_permutation 后,原数组的不变。

  5. 贪心算法

    贪心算法就是遵循某种规则,不断贪心地选取当期那最优策略的算法设计方法。

    如果我们不慎重地选择一个正确的规则,就会得到错误的算法。

    如果问题能够用贪心算法来求解的话,那么它通常是非常高效的。

    1. 区间问题:在可选工作中,每次都选择结束时间最早的工作。
    2. 字典序比较类型的问题经常用得上贪心法
  6. 动态规划

    1. 记忆化搜索

      在需要剪枝的情况下,可能会把各种参数都写在函数上,但是这种情况下会让记忆化搜索难以实现,需要注意

    2. def

      一步步按顺序求出问题的解的方法。

  1. memset 初始化数组

    语法: memset(<数组名>, <初始化的值>, <数组的大小>)

    虽然 memset 按照1字节为单位对内存进行填充,-1的每一位二进制位都为1,所以不可以像0一样用 memset 进行初始化。通过使用 Memset 可以快速地对高维数组等进行初始化,但是需要注意无法初始化成1之类的数值。

  2. 移位运算

    1. $1 << n = 2^n$
    2. $n << 1 = 2n$
    3. $n >> 1 = \lfloor{\frac{n}{2.0}}\rfloor$
  3. $a^b算法$

    求a的b次方对p取模的值,其中 $1 \leq a, b, p \leq 10^9$

    1
    2
    3
    4
    5
    6
    7
    8
    int power(int a, int b, int p) {
    int ans = 1 % p;
    for (; b; b >>= 1) {
    if (b & 1) ans = 1ll *ans * a % p;
    a = 1ll * a * a % p;
    }
    return ans;
    }
  4. $a * b % p$

    求a乘b对p取模的值,其中 $1 \leq a, b, p \leq 10^{18}$

    1. 方法一
      1
      2
      3
      4
      5
      6
      7
      8
      long long mul(long long a, long long b, long long p) {
      long long ans = 0;
      for (; b; b >>= 1) {
      if (b & 1) ans = (ans + a) % p;
      a = a * 2 % p;
      }
      return ans;
      }
    2. 方法二 ($P_6$)
      1
      2
      3
      4
      long long mul(long long a, long long b, long long p) {

      }

  5. 二进制状态压缩

    操作 运算
    取出整数n在二进制表示下的第k位 (n >> k) & 1
    取出整数n在二进制表示下的第0 ~ k - 1位(后k位) n & ((1 << k) - 1)
    把整数n在二进制表示下的第k位取反 n xor (1 << k)
    对整数n在二进制表示下的第k位赋值为1 n | (1<<k)
    对整数n在二进制表示下的第k为赋值为0 n & (~(1 << k))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"pyheader": {
"prefix": "test",
"body": [
"/**",
" * @author: test",
" * @desc:$1",
" * @date: ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} ${CURRENT_HOUR}:${CURRENT_MINUTE}:${CURRENT_SECOND}",
" * @Email:test@qq.com",
" * @url:test.com",
" */",
"",
""
],
"description": "test"
}
}
  1. 打开 vs­code 在左下角设置图标 -> 用户代码片段
  2. For_Loop: 当前 snip­pet 名字。
  3. pre­fix: 前缀,代码块使用快捷方式;键入前缀,按 tab 键,代码块就会被使用。
  4. body: 代码块内容;换行使用 rn。
  5. de­scrip­tion: 键入前缀,vs­code 感知到前缀,显示的说明内容。
  6. $1,$2,$0: 指定代码模块生成后,编辑光标出现位置;使用 Tab 键进行切换 (编辑光标按 $1,$2,$3...$0 的顺序跳转),$0 是光标最后可切换位置。

Snippet语法

Tabstops

$1,$2 指定代码块生成后,光标出现的位置;不同位置的相同 $1 位置同时出现光标。

Placeholders

给光标出现位置加上默认值;例如,${1:an­other\${2:place­holder}}$1 处位置默认值是 an­other

Choice

光标位置设置多个值可供选择;例如,${1|one,two,three|}$1 位置处可以选择 one,two,three 中一个词填充在此处。

Variables

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
常用变量
TM_SELECTED_TEXT 当前选中内容或空字符串
TM_CURRENT_LINE 当前行内容
TM_CURRENT_WORD 光标处字符或空字符串
TM_LINE_INDEX 从0开始的行号
TM_LINE_NUMBER 从1开始的行号
TM_FILENAME 当前被编辑文档名
TM_FILENAME_BASE 当前被编辑文档名,没有后缀
TM_DIRECTORY 当前被编辑文档目录
TM_FILEPATH 当前被编辑文档全路径
CLIPBOARD 当前剪切板内容

日期和时间相关变量
CURRENT_YEAR 当前年
CURRENT_YEAR_SHORT 当前年后两位
CURRENT_MONTH 月份,两位数字表示,例如02
CURRENT_MONTH_NAME 月份全称,例如 'July'
CURRENT_MONTH_NAME_SHORT 月份简写 ,例如'Jul
CURRENT_DATE 某天
CURRENT_DAY_NAME 星期几, 例如'Monday')
CURRENT_DAY_NAME_SHORT 星期几的简写, 'Mon'
CURRENT_HOUR 小时,24小时制
CURRENT_MINUTE 分钟
CURRENT_SECOND 秒数

  1. 理解

    网上的解释是 由ubuntu系统自动创建的回收站文件夹,每个盘都有一个 。s
    也就是说,系统在删除某个盘里的垃圾文件的时候,是创建一个 .Trash-1000文件夹,然后将它放进去。

  2. 例子

    比如 我在u盘上执行了删除操作,ubuntu系统会在u盘里也创建一个.Trash-1000文件夹,info文件夹应该是保存的是垃圾文件的对应关系,files文件夹里应该是删除的垃圾文件。

  3. 管理

    如果在 ubuntu系统里–清除回收站,各个盘里对应的垃圾文件就会自动删除了。

  1. 首先关闭 IDM 程序,IDM 安装目录,将 IDMGCExt.crx 文件拖入扩展管理面板进行安装,再启动IDM正程序
  2. 下载 - 选项,勾选上 使用高级浏览器集成,如果已经勾选,取消掉,再勾选,最后,手动添加浏览器,定位到Chrome的主程序,确定保存,重启浏览器。

  1. 新建 shadowsocks.desktop

  2. 将以下内容根据自己的情况更改,复制到 shadowsocks.desktop 内:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [Desktop Entry]
    Version=1.0
    Type=Application
    Name=Shadowsocks
    Comment=Connection Manager — Shadowsocks-Qt5
    Exec=/home/gph/shadowsocks_ubuntu/Shadowsocks-Qt5-3.0.1-x86_64.AppImage // AppImage程序路径
    Icon=/home/gph/shadowsocks_ubuntu/shadowsocks.png // 小飞机的图标
    Terminal=false
    StartupNotify=true
    Categories=Application
  3. 即创建快捷方式

Note: 如果没有创建快捷方式,好像应该不能开机自启。

  1. 安装zsh及配置(运行下面命令行)

    1. sudo apt-get install zsh -y
    2. git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh
    3. cp ~/.zshrc ~/.zshrc.backup
    4. cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc
    5. chsh -s /bin/zsh
    6. reboot // 重启
    7. vim .zshrc
    8. 修改主题: ZSH_THEME="agnoster" // 苹果主题(或者 “ys”: 不错的主题),此时为乱码

      参考链接

  2. 安装 powerline 字体

    1. git clone https://github.com/powerline/fonts.git --depth=1
    2. cd fonts
    3. ./install.sh
    4. cd ..
    5. rm -rf fonts

      参考链接

  3. 修改 terminal 字体为: droid sans mono dotted for powerline regular

    参考链接

  4. vscode下zsh乱码

    1. cd /usr/share/fonts/truetype/
    2. sudo git clone https://github.com/abertsch/Menlo-for-Powerline.git
    3. sudo fc-cache -f -v
    4. 设置vscode中的字体为 Menlo for Powerline

  • 搬瓦工

  1. (安装魔改BBR)输入:wget -N --no-check-certificate https://raw.githubusercontent.com/FunctionClub/YankeeBBR/master/bbr.sh && bash bbr.sh install
  2. 安装到蓝底窗口按TAB键选【NO】,选择重启【Y】,这里会断开连接,大家可以关掉窗口再重新打开,重新连接。
  3. (启动BBR)输入:bash bbr.sh start
  4. (下载SSR服务器端)输入:wget --no-check-certificate https://raw.githubusercontent.com/teddysun/shadowsocks_install/master/shadowsocksR.sh && chmod +x shadowsocksR.sh
  5. (设置SSR服务)输入:./shadowsocksR.sh,然后一直:回车

视频教程:点击这里

网页教程:点击这里

  • win10

  1. shadowsocks客户端下载:点击这里

视频教程:点击这里(同上)

  • ubuntu

  1. shadowsocks客户端下载:点击这里
  2. 将Shadowsocks.AppImage 移到 主目录或者Documents里面
  3. chmod a+x Shadowsocks.AppImage 赋予权限
  4. 安装python:
    1
    2
    sudo apt install python
    sudo apt install python3
  5. 设置-网络-网络代理:socks主机:127.0.0.1 1080

视频教程:点击这里

  • Android

  1. shadowsocks客户端下载:点击这里