Java学习笔记【保持更新】

匿名 (未验证) 提交于 2019-12-02 21:52:03

主要参考资料:https://www.bilibili.com/video/av56034035;
https://www.bilibili.com/video/av61604219
本文仅用于学习,如有侵权,请联系我,一定删除

  1. 语言特性

    由C和C++继承了许多成分,变量声明、操作符、参数传递和控制流程等完全相同。

    纯粹的面向对象

    舍弃了指针(以引用取代)、运算符重载、多重继承等

    增加了垃圾回收、泛型编程、类型安全的枚举、不定长参数和自动装/拆箱

    易学

    分布式

    健壮:丢弃指针,自动回收(免去了程序员忘记回收的问题)

    开源社区成熟

    解释型

    原生多线程

  2. JAVA技术平台

    SE:俗称核心JAVA

    JAVA学习流程

    Java是一个统称 包含javase 与Javaee(还有一个几乎没人用的javame) 是Sun公司,也就是现在的Oracle(把Sun给收购了)公司的一门面向对象的开源语言。
    javase 是java的一部分,也是基础的基础,主要是用来做桌面程序的,就像我们平时下载用的那些软件,也就是楼上说的那些客户端程序。但是用Java做软件的话,前途不明朗。
    jsp 是一种动态的页面,运行于服务器(tomcat、 jboss等),里面可以嵌套有html 或javase、Javascript的代码。
    至于javaweb那就大了,它包含了以上所有的东西,并且还有一些框架(例如 spring struts hibernate ibatis 。。。。)关于开发javaweb的框架大约有几百种之多(当然平常你掌握几种就可以了,其他的框架,用到了,现学就可以了)。
    要想学好Javaweb,Javase这个基础的基础必须要打好。尤其是Javase里面集合,io流,网络。。。等这几大块是重点的重点。
    至于学习顺序 Javase --》jsp --》javaweb。
    看到jsp的时候,可以写一些页面的东西,算是基本的web程序吧。

  3. JAVA核心机制

  4. 垃圾回收(GC)

    C++手动回收的优点:能够在内存不使用时快速回收,准确高效

    缺点:容易失误,出现bug

    JAVA使用系统级线程自动检测回收,优点:自动,不会出现bug

    缺点:回收不及时(在硬件水平较高的现在,可以忍受:宁可回收不及时,但是一定要回收

  5. 题外话:Windows x86是32位系统,x64是64位系统

    狭义的x86架构,指只支持32位的intel/AMD/VIA的CPU,并向下兼容16位(实模式);

    狭义的x64架构,指的是支持32位和64位的intel/AMD CPU,指令集与x86兼容,并向下兼容16位(实模式),目前绝大多数民用CPU和服务器CPU都是这样的;

    狭义的ia64架构,指的是安腾系列的CPU,虽然指令集也是64位的,但不兼容32位,intel独有的,这种CPU比较少见,基本不生产了,近似于淘汰的状态;

    广义的x86架构,泛指支持x86和x64架构intel、amd的CPU,但不包含ia64(安腾)。

    目前市面上能看到的E5没有安腾架构的(安腾是独立发布的),绝大多数E5都支持x64

  6. 正规的开发工作中,不同的任务对JAVA版本的要求不一样,同时进行多个任务时版本不同会出问题:使用压缩版jdk,根据情况解压不同版本使用

    换开发版本时,在环境变量中改JAVA_HOME即可(将其改为对应版本的文件夹)所以要用压缩版

  7. JDK与JRE

  8. 编译过程

    .java->.class->.exe

  9. 注意代码规范和可读性!!

  10. Java语言的一些要点

    • Main方法的固定书写格式:

      public static void main(String[]args){...}

    • 严格区分大小写

    • 语句以;结束

  11. 常见问题及解决办法

    声明为public的主类必须与文件名一致

  12. 注释

    • 注释类型

      @author:写作者

      @version:写版本

  1. 保留字

    尽量不要在编程中使用保留字

  2. 标识符

    • 标识符要求

      数字不可以开头

    • 命名规范(很重要)

      总的来说可以分为四类:

      1. 包名所有字母小写常 2.变量名所有字母大写,单词间下划线连接 3.类名、接口名使用大驼峰命名法 4.变量名、方法名使用小驼峰命名法
  3. 变量

    • 保存在内存中

    • JAVA中的每个变量都必须先声明后使用

    • 变量在一对{}中有效

    • 数据类型

      声明long型常量需要后加'l'或'L',float型后加'f'或'F'

    • Boolean类型:只能使用true或false,不能用0或者非0来定义

    • 引用类型,初始化时都可以初始化为null

    • 字符串值不可变

      这里的s0和s1、s2都引用了同一个字符串同样的字符串只会储存一遍(注意如s2这样的也是一样的)

  1. Workspace:利用工作空间维护不同项目
  1. 数据类型转换

    要点:1. 计算时向上转换

         2.byte\short\char均转换为int       3.任何基本类型与字符串连接,均转为字符串
  2. 自增自减

自增自减运算符区分

符号在前:先运算再取值

在后:先取值再运算

  1. ȡģ
  • 模数为负时,负号可忽略;但被模数为负则不可忽略,应正常运算
  1. char类型与string类型的区分

单引号引起来的是字符(char),双引号引起来的是字符串(string)

第一个,char在+运算符中转换为了int,做了数学运算

第二个因为第一个是string,在+运算符中做了向上的数据类型转换,转换为了string,做了字符串拼接

  1. 拓展赋值运算符

除了加减,乘除、取模其实都有拓展赋值运算符的!

这是个之前没有注意过的点,对变量进行运算时要考虑到其类型的变化,否则会报错(可能是JAVA的特性?)

所以,自增的时候用自增运算符比用加法语句更好

  1. 逻辑运算符

  • 异或:相同是为false,不相同时为true

  • JAVA中判断条件只能是单向的

  • 逻辑与(&)和短路与(&&)的区别:

    之前在C++中常用的都是短路运算符

    在不需要逻辑运算两边都参与运算的时候(也就是绝大多数时候),尽量使用短路运算符&&和||

  1. 位运算符

    用的比较少其实

    有符号的右移时看首尾,是0补0,是1补1

    无符号均补0

    解释:

  2. 三目运算符

    (条件表达式)?表达式1:表达式2;

    如果条件表达式为true,则执行表达式1,否则执行表达式2

  3. 运算符的优先级

  1. 一位数组的声明方式:

    int a[];或者int[] a;

  2. 初始化:

    1. 动态初始化
    • int[] arr=new int[3];
    • arr[i]=x;i=0,1,2
    1. 静态初始化
    • int a[]=new int[]{3,9,8};
    • int[] a={3,9,8};
      注:对于int[] a和int a[]这两种方式,上面的初始化方法都是可以的
  3. 数组初始化后,每个数组都有一个length属性指明它的长度(和C++不同,可以直接调用属性获得长度)
  4. 使用动态初始化时,数组的元素默认值是0,对象的默认类型为null

  5. 多维数组

    相比C++,有了原生支持的多维数组

    可以只定义n-1维的大小,留下一个未定义的维度

  6. 特殊写法:

    int[] x,y[];这里的x是一维数组,而y是二维的(有点反直觉2333)

  7. 总结:一维数组和二维数组的各种写法

    1. 一维数组:

      1. int[]x
    2. int x[]

    3. 二维数组:

      1. int[][][] []y

      2. int[]y[]

      3. int y[] []

        反正就是要凑够两个括号,这两个括号怎么搁都是可以的

  1. 使用包的必要性
    • 解决重名问题
    • 便于管理
  2. package中用.表示包的层次(包的概念等同于文件夹的概念,可以有多级)
  3. 可以Import具体的包下的对象
  1. 实例化:

    不实例化,就是一个空指针

  2. 对变量的分类

    实例变量只有实例化之后才能使用,而类变量直接用类名就可以使用

    成员变量在堆,局部变量在栈

  3. 匿名对象

    new Person().shout();

    使用场景:

    1. 对一个对象只需要一次方法调用
    2. 作为实参传递给方法
  4. 方法的重载

    参数的个数或者参数的数据类型必须不同

  5. 可变个数的形参

    1. (String[] args)//定义字符串数组
    2. (String... args)//JAVA特有的点点点的方式,这种参数在使用时与数组的使用方式相同

    两种方法的区别:

    第一种如果没有参数,需要输入null或者一个空数组;而第二种直接不填就好(...代表可以传递0到多个参数)

  6. 方法的参数传递

    JAVA只有一种参数传递方法:值传递。即将实际参数值复制到方法的形参中,而参数本身不受影响(当然这种方法比较慢,但是JAVA吗23333)

    注意,对于引用对象,值还是变化了的,因为传进去的实际是地址

  7. this关键字

    使用this的几个优点:

    1. 不需要给方法中的形参乱起名字了

      如图,使用this的话,形参的名称可以和成员变量相同,赋值的时候借助this区分就好,这样就不需要在函数的形参上乱起名字了

    2. 增强程序阅读性

    3. this可以作为一个类中,构造器相互调用的特殊格式

      用于构造器重载时,可以直接调用已有的构造器然后进行修改即可,使构造器的编写更加方便

      注意,这种情况下,this()必须放在构造器的首行(显然)

  8. JAVA Bean

    右键-》source-》general getters and setters,自动生成get和set方法

  9. 继承(extends)和多态

    JAVA只支持单继承

    继承让类与类之间产生了关系,不要仅仅为了获取其他类中的某个功能而去继承-》继承是要有逻辑关系在其中的

  10. 方法的重写(@override)

    • 重写只能重写方法体,名称、参数列表和返回值类型均不能改
    • 重写方法不能使用比被重写方法更加严格的访问权限
    • 子类方法抛出的异常不能大于父类被重写方法的异常

    题外话:ctrl+/:eclipse快速注释代码

    如果子类和父类在同一个包下,那么对于父类的成员修饰符除了private之外,剩下的情况子类都可以使用父类的成员变量;如果不在一个包下,只有protected和public的成员变量可以使用

  11. super关键字

    super和this类似,但是是用于调用父类的成员变量和方法的。

    使用super,子类可以调用父类之上的所有父类层级

    this和super的区别:

    在子类中,通过This或者super调用构造器,只能使用一个,因为都要占据第一行

  12. 类对象的实例化过程

    有继承时:

    在方法区,先加载父类方法,再加载子类方法

    子类构造方法先入栈,父类再入栈,所以父类先执行

  13. instanceof操作符

    instance:实例

    属于子类也行

  14. Object类


    也叫基类
    形参定义为object类型,则可以接受任何类型的类(注意,必须是个类!)

    • equal:比较引用对象:是否引用了同一个对象(也就是一个new出来的对象)

      Person e=new person(); Person p=new person();//此时e和p指向的是堆中的两个不同的对象,此时用equal方法检测是false e=p;//此时e被重定向到p,此时再检测就是true了
  15. 对象的类型转换

    能进行的是有继承关系间的类型的转换

    从子类到父类:

        ![1567935647850](https://s2.ax1x.com/2019/09/09/ntxdEj.png)

    由此可见,object对象可以接收任何类型的对象

    从父类到子类:

        ![1567935689642](https://s2.ax1x.com/2019/09/09/ntxwUs.png)

    示例代码:

    e可以接受父类为Person类的各种类的对象,然后检查其是否是Student类或Student类的子类,如果是,就执行Student类中独有的方法,否则执行其他方法。

  16. ==操作符和equals方法

    只有指向同一个对象,==才为true,而比较的不是成员变量的值是否相等

    equals和==功能相同,除了下面说的几个特殊情况:

  17. String对象的创建

    可以看到,无论是哪一种方法,同样的字符串也不会保留多个

    --所以字面量创建更加省内存,所以其常用

    而“不重复”只全体现在常量池上,如果使用New的方式,堆中每次都会创建一个新的对象。最特别的是最后一种情况,常量池中添加的是拼接钱的串,而堆中创建的却是拼接后的串

  18. 包装类

    将基本数据类型赋予了类的方法

    • 自动拆箱、装箱:和基本数据类型写法可以保持一致(转换更简单,使用起来比基本数据类型更方便、功能更强大)

    • 包装类的功能:

      借助包装类的类方法,将字符串转换为对应类型的数据(注意,只是借助其实现转换,并没有直接使用包装类)

      1. 字符串转其他基本数据类型

      int i=Integer.parseInt("123");

      boolean b=Boolean.parseBoolean("false");

      1. 其他基本数据类型转字符串

      String istr=String.valueOf(i);

      String batr=String.valueOf(b);

      ->综上,包装类主要应用于字符串和其他基本数据类型的应用

  19. 重写toString()

    toString方法是直接用System.out.printIn()打印时调用的方法。默认的toString()是类Object的方法,由于其是所有类的父类,所以其功能比较泛,该方法就是输出内存地址(大多数情况下这是没什么用的)。所以各个类可以重写该方法以打印有意义的内容。

  20. static关键字

    static用来设置类属性和类方法

    static一般只用Public修饰

    类属性被所有对象所共用,可以用来计数什么的

  21. 单例设计模式--设计模式之一

    • 什么是设计模式?

      设计模式就是实际编程中逐渐总结出来的一些解决问题的套路

    • 单例

      只有一个实例(实例化对象)

      在整个软件系统运行过程中,这个类只被实例化一次,以后不论在哪都只调用这一个实例

    • 应用的场景:

      1. 该对象的创建要消耗大量的时间和资源
      2. 重复new对象没有必要
    • 两种实现方式:

      1. 先构造后使用

        构造方法私有化,不能new

        实例化也是私有的,引用是私有的类变量

        利用公有的方法返回那个类变量

        这样的话,程序中永远只有一个实例化对象

      2. 先不构造,用的话再构造,如果从来不用就算了,就这一点区别

        先判断(s1==null)来判断是否需要实例化

  22. 理解main方法

     ![1567943755648](https://s2.ax1x.com/2019/09/09/ntxo26.png)

    例如cmd,即可给main传参

  23. 初始化代码块

    作用:对JAVA对象进行初始化

    类的成员初始化过程:声明成员变量默认值-》显示初始化,执行代码块-》执行构造方法

    • 静态代码块

      静态代码块用于初始化静态属性

      非静态代码块每次new都要重新执行,而静态代码块只执行一次。且静态先于非静态执行

    在实际应用中,静态代码块用的比较多,用于初始化静态类属性(尤其对于一些比较复杂的静态类属性,例如属性是一个类的情况下),非静态的作用不太大

  24. 匿名内部类

    对Person类的方法进行了重载,所以其其实是一个Person的子类,故称为匿名内部类

    匿名内部类中成员变量的初始化(如果要和父类的初始化有所不同的话)无法通过构造方法实现(因为没有类名,无法创建构造方法),只能通过代码块的方式完成

  25. final关键字

    概况一下,final修饰的内容初始化之后就不能再修改了,所以变量要均大写,和常量保持一致(final修饰的变量是常量,常量必须显式赋值

    final static:全局常量

  26. 抽象类

    含有抽象方法的类必须被声明为抽象类

    final和abstract是冲突的

    抽象类可以有构造方法

  27. 模板方法设计模式--设计模式之二

  28. 接口

    接口中只有定义,没有实现

    interface定义,implements使用

    类可以实现多个接口,多个接口之间用,分割

    当有一个新的需求时:

    • 如果父类新增抽象方法,子类就必须实现,否则就需要定义为抽象类。

    • 解决方法就是不要在父类中新增抽象方法,而是新增一个接口,由子类去选择是否实现这一接口

      父类需要稳定的抽象,不能总是在改

    需要描述一个交叉的关系时:

    • 不能使用类的继承解决这个问题:会污染类的继承,使继承关系没有逻辑性
    • 实现接口就好(接口是一类方法(动作)的集合

    对抽象类和接口的总结:

    • 抽象类是对于一类事物的高度抽象,其中既有属性也有方法
    • 接口是对方法的抽象,也就是对一系列动作的抽象
    • 当需要对一类事物抽象的时候,应该使用抽象类,好形成一个父类;当需要对一系列动作抽象时,就使用接口

    看到了以下一段代码:

    这里的Runnable是接口,我们知道接口是不能实例化对象的,那这里是什么情况呢?

    其实这不是声明了一个接口类型的对象,而是一种多态机制,专业名词叫“匿名内部类”,实际上是创建了一个遵守了该接口的普通类(Object)对象。

    类似的使用还有下面,可以看到,类能声明的接口几乎都可以声明

    interface  Shape {     void  draw(); } public class Main {   // interface type as instance variable   private Shape myShape;    // interface type as parameter type for a constructor   public Main(Shape s) {     this.myShape = s;   }    // interface type as return type of a method   public Shape getShape() {     return this.myShape;   }    // interface type as parameter type for a method   public void setShape(Shape s) {     this.myShape = s;   }    public void letItSwim() {     // interface type as a local variable     Shape locaShape = null;      locaShape = this.myShape;      // interface variable can invoke methods     // declared in the interface and the Object class     locaShape.draw();   } }

    这其实是一种变相的“用接口作为类型”,也是面向接口的编程的思想之一。

  29. 工厂模式――设计模式之三

    在真正的开发工作中,都是合作开发,每个开发人员写一部分,集合到一起成为一个项目

    问题:一个开发人员要改代码,例如改类名,会影响其他人的工作。因此降低了工作效率

    解决方法:写一个产品接口类,一个工厂接口类,其他人用工厂类而不是直接用产品类。通过工厂将new对象隔离,通过产品的接口接受不同实际产品的实现类,实例的类名的改变不影响其他合作开发人员的编程。其他开发人员只需要关注工厂类的使用,而修改代码只限于产品类的名称和工厂类的代码,不会影响工厂类的使用。(这种设计可能是架构师的任务(?ω?))

    无论是内部类还是其他,JAVA除了很早期的一些特征外,剩下的特征都是为了解决一些特点问题必不可少的,不是冗余的功能

  30. 内部类

    内部类是外部类的成员

    注意外部类调用内部类时方法

    内部类可以声明private 或者protected

    内部类的声明和外部类互相不影响,可以重名

    内部类的作用:

    • 内部类的最大作业是实现多重继承(A想同时获得类B和类C的方法并重写)
  31. Lambda表达式

    关于Lambda表达式的详细内容,参见https://segmentfault.com/a/1190000009186509

    空括号用于表示一组空的参数。例如 () -> 42

    当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 a -> return a*a

    Lambda表达式的应用:

    线程可以初始化如下:

    // Old way new Thread(new Runnable() {     @Override     public void run() {         System.out.println("Hello world");     } }).start();  // New way new Thread(     () -> System.out.println("Hello world") ).start();

    事件处理可以用 Java 8 使用 Lambda 表达式来完成。以下代码显示了将 ActionListener 添加到 UI 组件的新旧方式:

    // Old way button.addActionListener(new ActionListener() {     @Override     public void actionPerformed(ActionEvent e) {         System.out.println("Hello world");     } });  // New way button.addActionListener( (e) -> {         System.out.println("Hello world"); });

    输出给定数组的所有元素的简单代码。请注意,还有一种使用 Lambda 表达式的方式。

    // old way List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); for (Integer n : list) {     System.out.println(n); }  // 使用 -> 的 Lambda 表达式 list.forEach(n -> System.out.println(n));  // 使用 :: 的 Lambda 表达式 list.forEach(System.out::println);

    输出通过逻辑判断的数据。

    package com.wuxianjiezh.demo.lambda;  import java.util.Arrays; import java.util.List; import java.util.function.Predicate;  public class Main {      public static void main(String[] args) {         List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);          System.out.print("输出所有数字:");         evaluate(list, (n) -> true);          System.out.print("不输出:");         evaluate(list, (n) -> false);          System.out.print("输出偶数:");         evaluate(list, (n) -> n % 2 == 0);          System.out.print("输出奇数:");         evaluate(list, (n) -> n % 2 == 1);          System.out.print("输出大于 5 的数字:");         evaluate(list, (n) -> n > 5);     }      public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {         for (Integer n : list) {             if (predicate.test(n)) {                 System.out.print(n + " ");             }         }         System.out.println();     } }

    运行结果:

    输出所有数字:1 2 3 4 5 6 7  不输出: 输出偶数:2 4 6  输出奇数:1 3 5 7  输出大于 5 的数字:6 7 

    java.util.stream.Stream接口 和 Lambda 表达式一样,都是 Java 8 新引入的。所有 Stream 的操作必须以 Lambda 表达式为参数。Stream 接口中带有大量有用的方法,比如 map() 的作用就是将 input Stream 的每个元素,映射成output Stream 的另外一个元素。

    下面的例子,我们将 Lambda 表达式 x -> x*x 传递给 map() 方法,将其应用于流的所有元素。之后,我们使用 forEach 打印列表的所有元素。

    // old way List<Integer> list = Arrays.asList(1,2,3,4,5,6,7); for(Integer n : list) {     int x = n * n;     System.out.println(x); }  // new way List<Integer> list = Arrays.asList(1,2,3,4,5,6,7); list.stream().map((x) -> x*x).forEach(System.out::println);

    下面的示例中,我们给定一个列表,然后求列表中每个元素的平方和。这个例子中,我们使用了 reduce() 方法,这个方法的主要作用是把 Stream 元素组合起来。

    // old way List<Integer> list = Arrays.asList(1,2,3,4,5,6,7); int sum = 0; for(Integer n : list) {     int x = n * n;     sum = sum + x; } System.out.println(sum);  // new way List<Integer> list = Arrays.asList(1,2,3,4,5,6,7); int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get(); System.out.println(sum);

    Lambda表达式和匿名类之间的区别

    • this 关键字。对于匿名类 this 关键字解析为匿名类,而对于 Lambda 表达式,this 关键字解析为包含写入 Lambda 的类。
    • 编译方式。Java 编译器编译 Lambda 表达式时,会将其转换为类的私有方法,再进行动态绑定。
  32. 双冒号(::)操作符

stackOverFlow:栈溢出

编程时主要面对的就是运行时错误

常见异常:

程序员能解决的往往也只是Exception而不是Error

try-catch模型:

finally里的是无论异常还是不异常都执行的部分

在try中的代码,报错后就结束,不会执行try中剩下的部分

throw exception

抛出异常后在调用位置去捕获处理

main方法也可以抛出异常,但是抛出就直接甩到虚拟机了,程序中对其将无法处理,所以一般不这样做

重写方法不能抛出比被重写方法范围更大的异常类型,也就是说,子类不能抛出比父类更大范围的异常

人工抛出异常:

![1567958149640](https://s2.ax1x.com/2019/09/09/ntztRx.png)

用户自定义异常类必须继承现有异常类

JAVA提供的异常类一般是够用的,只有特殊情况才需要自定义,情况少见

集合只能存放对象是引用

HashSet

不可重复,指的是hashcode是否相同,而不是equals是否相同

set.size();//获取集合元素个数

如果想让集合只能存相同类型的对象:使用泛型

Setset1= new HashSet();

TreeSet

TreeSet默认是排序了的

如图,如果要在TreeSet中放入自定义类型的对象,则必须实现compare方法

使用TreeSet必须放入相同类型对象

Set set=new HashSet();

Iterator it= set.iterator();

//迭代输出法一

while(it.hasNext()){

Systems.out.printLn(it.next());//注意,调用next()会使迭代器加一

}

//迭代输出法二:foreach法(注意虽然思想是foreach,但是代码中还是只有for没有each)

for(Object obj:set){//将set中的每一个值取出来,赋值给obj

Systems.out.printLn(obj);

}

List

Listlist=new ArrayList();

add添加数据,addAll在指定的下标位置添加数据

Vector较老,现在基本不使用

Map

put(key,value);

根据key可以修改value或者移出键

  • 遍历Map――法一

    1. map.keySet//获取map所有key的集合
    2. map.valueSet

    通过map.KeySet来遍历:

  • 遍历Map――法二

    一般使用map集合,不会使用过于复杂的对象做key

  1. 泛型类

    就是C++中的模板,但是使用上会方便一点

    泛型类中可以定义泛型变量

  2. 泛型接口

    类也就因此变成了泛型类

  3. 泛型方法

    在public之后跟上要定义的泛型,则可以在方法中的任何位置使用泛型了

    在静态方法中,不能使用类定义的泛型,只能使用该方法自己定义的泛型

有限制的通配符可以为参数类型给出一些限制

第三个比较有用:只有实现了Comparable接口才能传入,这样方法内部就可以使用Comparable的方法而保证所有传入的对象都可以操作了

示例:

每一个枚举,都是调用了一次构造方法

使用“枚举类名.枚举”相对于调用了构造方法。但每次执行获得的都行相同对象:枚举类中的每个枚举都是单例的

娉ㄨВ

@Deprecated过时方法不是不能调用,只是显示出来,便于选择和之后的迭代

@Target(ElementType.FIELD)//声明这个注解类是给其他类的属性做注解

@Rectention(RetentionPolicy.RUNTIME)//定义注解的生命周期

@Documneted//表示将注解写到文档中

@interface TestAnn{

public int id() default 0;//default是默认值

public String desc() default "";

}

使用:

对一个属性进行注解

进程之间资源不共享,所以在程序中一般不单独开辟进程

线程是一个任务执行的最小单元

线程的并发和进程是一样的,也是CPU通过中断进行“假并发”

多个线程同时访问的资源叫临界资源

题外话:时间片

时间片(timeslice)又称为“量子(quantum)”或“处理器片(processor slice)”是分时操作系统分配给每个正在运行的微观上的一段CPU时间(在抢占中是:从进程开始运行直到被抢占的时间)。时间片通常很短(在Linux上为5ms-800ms)

时间片由内核的分配给每个进程。首先,内核会给每个进程分配相等的初始时间片(在Linux系统中,初始时间片也不相等,而是各自父进程的一半),然后每个进程轮番地执行相应的时间,当所有进程都处于时间片耗尽的状态时,内核会重新为每个进程计算并分配时间片,如此往复。

系统通过测量进程处于“睡眠”和“正在运行”状态的时间长短来计算每个进程的交互性,交互性和每个进程预设的静态(Niceֵ)的叠加即是动态优先级,动态优先级按比例缩放就是要分配给那个进程时间片的长短。一般地,为了获得较快的响应速度,交互性强的进程(即趋向于)被分配到的时间片要长于交互性弱的(趋向于)进程。

抢占式多任务处理(Preemption)是计算机中,一种实现(multi task)的方式,相对于而言。协作式环境下,下一个进程被调度的前提是当前进程主动放弃时间片;抢占式环境下,操作系统完全决定调度方案,操作系统可以剥夺耗时长的进程的时间片,提供给其它进程。

  • 每个任务赋予唯一的一个优先级(有些操作系统可以动态地改变任务的优先级);
  • 假如有几个任务同时处于就绪状态,优先级最高的那个将被运行;
  • 只要有一个优先级更高的任务就绪,它就可以中断当前优先级较低的任务的执行;

一个线程就是一个程序内部的顺序控制流

java.lang.Thread模拟CPU实现线程,所要执行的代码和处理的数据要传递给Thread类。

  • 法一:线程实例化
  1. 继承Thread类,做一个线程子类(自定义的线程类)

  2. 重写run方法,将需要并发执行的任务写到run中

  3. 线程开辟需要调用start方法,使线程启动,来执行run中的逻辑(如果直接调用run方法,这不会开辟新线程,或者线程不会进入就绪态,没有实现多线程)

    多线程的效果:

    主线程先结束,再执行逻辑

    这种方式可读性更强

    缺点:由于JAVA的单继承,则该类无法再继承其他类,会对原有的继承结构造成影响

  • 法二:通过Runnable接口

    不影响原有继承关系

    缺点:可读性较差

  1. 线程的命名

    1. 法一:使用setName命名

    2. 法二:在构造方法中直接命名

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!