Java笔记

基础阶段

Java用途

![Java应用广泛.png](https://img-blog.csdnimg.cn/img_convert/2fb0e6a9772ad5c231e76f3b4adcd5f5.png#averageHue=#1f1f1f&clientId=u3c6a5d5b-9e0f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=263&id=u101a251a&margin=[object Object]&name=Java应用广泛.png&originHeight=263&originWidth=1721&originalType=binary&ratio=1&rotation=0&showTitle=false&size=28072&status=done&style=none&taskId=u67ebe3b2-3530-474d-9843-a364e11d2e6&title=&width=1721)

Java 和 Python 各自定位

Java 源自 C 和C++体系,1995年推出,专门给程序源设计的,大型的复杂应用的
Python 在1991年推出,主要面向系统管理,科研教育,非程序员应用的

什么是Java

![Java体系.png](https://img-blog.csdnimg.cn/img_convert/ddbbb9c21cd2d411470d45b11dac9a89.png#averageHue=#f8f8f8&clientId=u3c6a5d5b-9e0f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=3366&id=u058e7d52&margin=[object Object]&name=Java体系.png&originHeight=3366&originWidth=714&originalType=binary&ratio=1&rotation=0&showTitle=false&size=309390&status=done&style=none&taskId=u0e4017ad-2d6c-4c3f-9e5f-b0956520e53&title=&width=714)

冯诺依曼体系结构

![image.png](https://img-blog.csdnimg.cn/img_convert/da309658aad34dfec029634734792dad.png#averageHue=#f9eede&clientId=u3c6a5d5b-9e0f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=196&id=ufd21a35e&margin=[object Object]&name=image.png&originHeight=196&originWidth=440&originalType=binary&ratio=1&rotation=0&showTitle=false&size=72528&status=done&style=none&taskId=ub04d04d6-96b6-42d0-b9a5-911cdf1d758&title=&width=440)

软件及软件开发

计算机可以使计算机按照事先预定好的顺序完成特定的功能
计算机按照功能划分为 系统软件 和 应用软件
系统软件
DOS(Disk Operating System),Windows,Linux,Unix,Max,Android,IOS
应用软件
WPS,QQ,微信,英雄联盟,绝地求生…

基本的DOS命令

开启DOS控制台的几种方式

1、开始+系统+命令提示符
2、Win +r 输入CMD
3、在任意文件夹下面,按住shift + 鼠标右键 ,代开在此处打开命令行窗口
4、在资源管理器的地址栏前加上cmd 路径下输入
管理员身份运行,选择以管理员方式运行

常见DOS命令

// 盘符切换,直接写全路径切换
// 查看当前目录下的所有文件 dir
// 切换目录 cd change dir; cd /d dir跨盘符
// 清屏 cls (clear screan)
// 推出终端 exit
// 查看电脑IP ipconfig
// 打开应用
calc 计算机
mspalint 画板
notepad 笔记本
// 文件操作命令
创建文件夹 md 文件名
创建文件 cd> a.txt
删除文件 del 文件名
移除文件 rd 目录名

计算机语言的发展史

第一代语言

机器语言,二进制语言

第二代语言

汇编语言
解决人类无法读懂机器语言的问题,指令代替二进制
目前应用:逆向工程、机器人、病毒

第三代语言

摩尔定律,当价格不变时,集成电路上可容纳的晶体数目,约每个18个月便会增加一倍,性能也将提升一倍,换而言之,每一美元所能买到的电脑性能,将每个18个月翻两倍以上

高级语言

大体上分为:面向过程和面向对象两类
C语言是典型的面向过程的语言。C++\JAVA是典型的面向对象的语言

初识JAVA

故事:Java帝国的诞生

1972年C诞生
贴近硬件,运行极快,效率极高。
操作系统,编译器,数据库,网络系统等
指针和内存管理
1982年C++诞生
面向对象
兼容C
图形领域、游戏等
我们要建立一个新的语言∶
语法有点像C
没有指针
没有内存管理
真正的可移植性,编写一次,到处运行
面向对象
类型安全高质量的类库
1995年的网页简单而粗糙,缺乏互动性。
图形界面的程序(Applet)
Bill Gates说:这是迄今为止设计的最好的语言!

Java 2标准版(J2SE):去占领桌面
Java2移动版(J2ME):去占领手机
Java 2企业版(J2EE):去占领服务器

大量的巨头加入

他们基于Java开发了巨多的平台,系统,工具
构建工具:Ant,Maven,Jekins
应用服务器: Tomcat,Jetty,Jboss, Websphere, weblogic(集群功能,展示了复杂应用的 可扩展性高应用性【高可用,高性能,高并发(三高中间件的东西,把程序员从事务管理,安全管理,权限管理解放出来,可以专注业务进行开发)】)
Web开发:Struts,Spring,Hibernate, myBatis
开发工具:Eclipse, Netbean,intellij idea, Jbuilder
2006 : Hadoop(大数据领域)
2008 : Android(手机端)

Java特性和优势

特性

简单性

是c++语法的纯净版,没有头文件,没有指针运算,不用分配内存,语法基于C,学习简单

面向对象

程序设计技术,重点在对象以及对象的接口上,模拟人的思维写程序

可移植性

跨平台性(write once,run anywhere)

高性能

即时编译,效率高

分布式

处理 TCPIV 协议,通过URL 统一资源定位,访问网络资源

动态性

通过反射机制,使java 有动态性

多线程

交互行为和事实行为

安全性

防病毒防篡改的系统

健壮性

去掉指针内存的管理,异常机制

Java 三大版本

JavaSE:标准版(桌面程序,控制台开发)
JavaME:嵌入式开发(手机,小家电)
JavaEE:E企业级开发(web端,服务器开发)

JDK,JRE,JVM

JDK : Java Development Kit java开发工具
JRE : Java Runtime Environment java运行环境
JVM : JAVA Virtual Machine java虚拟机

搭建开发环境

卸载JDK
1.删除Java的安装目录
2删除JAVA_HOME
3.删除path下关于Java的目录

  1. java -version
    安装JDK
    1.百度搜索JDK8,找到下载地址
    2.同意协议
    3.下载电脑对应的版本
    4.双击安装JDK
    5.记住安装的路径
    6.配置环境变量
    1-我的电脑–>右键–>属性
    2.环境变量–>JAVA_HOME
    3.配置path变量
    7、测试JDK是否安装成功
    1、打开cmd
    2、java -version

Helloworld

1.随便新建一个文件夹,存放代码
2.新建一个ava文件
。文件后缀名为.javao Hello.java
。【注意点】系统可能没有显示文件后缀名,我们需要手动打开I
3.编写代码

public class Hell0{
	public static void main(String[] args){
        System.out.print("Hello World!");
    }
}

4.javac Hello.java //编译java文件生成class文件
java Hello
情况分析:取分大小写;尽量使用英文;文件名和类名必须保证一直;符号使用中文

Java开发环境搭建

JDK下载与安装
配置环境变量
JDK目录介绍
HelloWorld及简单语法规则
Notepad++安装和使用

Java程序运行机制

编译型
解释型
程序运行机制

IDEA安装和介绍

IDE:https://baike.baidu.com/item/IDE/8232105?fr=aladdin
创建空文件
![image.png](https://img-blog.csdnimg.cn/img_convert/4890c492be425472cc6c48eb3884bfc7.png#averageHue=#eaefeb&clientId=u27ca2d9c-7c2d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=374&id=u2aa79025&margin=[object Object]&name=image.png&originHeight=374&originWidth=541&originalType=binary&ratio=1&rotation=0&showTitle=false&size=62523&status=done&style=none&taskId=u59159411-6d41-4d63-83be-56704da4622&title=&width=541)

![image.png](https://img-blog.csdnimg.cn/img_convert/cfe6546ba8f08037e08d67ff16064120.png#averageHue=#d4d0d0&clientId=u27ca2d9c-7c2d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=281&id=u24d48f78&margin=[object Object]&name=image.png&originHeight=281&originWidth=485&originalType=binary&ratio=1&rotation=0&showTitle=false&size=71415&status=done&style=none&taskId=u03d40d1a-da7f-44ab-8ed3-cd772ae3960&title=&width=485)

![image.png](https://img-blog.csdnimg.cn/img_convert/c4c2164094100fa0a965fe1591c999e3.png#averageHue=#e9e9e9&clientId=u27ca2d9c-7c2d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=463&id=u51f107e8&margin=[object Object]&name=image.png&originHeight=463&originWidth=747&originalType=binary&ratio=1&rotation=0&showTitle=false&size=48695&status=done&style=none&taskId=u8252e648-3539-48ce-a237-23daec8d324&title=&width=747)

![image.png](https://img-blog.csdnimg.cn/img_convert/1632d02be7977a8035ef39ffe7b50406.png#averageHue=#d8d7d5&clientId=u27ca2d9c-7c2d-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=391&id=u3c73e9ee&margin=[object Object]&name=image.png&originHeight=391&originWidth=685&originalType=binary&ratio=1&rotation=0&showTitle=false&size=93965&status=done&style=none&taskId=u3404b73c-5622-4631-a6cc-1441a9eea1a&title=&width=685)

标识符

关键字

abstract
assert
boolean
break
byte
case
catch
char
class
const
continue
default
do
double
else
enum
extends
final
finally
float
for
goto
if
implements
import
instanceof
int
interface
long
native
new
package
private
protected
public
return
strictfp
short
static
super
switch
synchronized
this
throw
throws
transient
try
void
volatile
while

标识符注意点

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

public static void =ainstring[ ] arg5》i
Steing 王者荣耀-最强王者";
System.out-peintln[王者荣耀;

可以使用中文命名,但是一般不建议这样去使用,也不建议使用拼音,很Low

数据类型

强类型语言
要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用弱类型弱类型语言

Java的数据类型分为两大类
基本类型(primitive type)
引用类型(reference type)

变量

变量是什么:就是可以变化的量!
Java是一种强类型语言,每个变量都必须声明其类型。
Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域。
type varName[=value] [i,varName[=value]}] ;
//数据类型变量名=值;可以使用逗号隔开来声明多个同类型变量。
注意事项:
每个变量都有类型,类型可以是基本类型,也可以是引用类型。变量名必须是合法的标识符。
变量声明是一条完整的语句,因此每一个声明都必须以分号结束

什么是子节

位(bit):是计算机内部数据储存的最小单位,11001100是一个八位二进制数。
字节(byte):是计算机中数据处理的基本单位,习惯上用大写B来表示,
1B (byte,字节) = 8bit(位)
字符:是指计算机中使用的字母、数字、字和符号

1bit表示1位,
1Byte表示一个字节1B=8b。1024B=1KB
1024KB=1M
1024M=1G.

类型转换

由于Java是强类型语言,所以要进行有些运算的时候的,需要用到类型转换。
低-------------------------->高
byte,short,char-> int -> long-> float -> double
运算中,不同类型的数据先转化为同一类型,然后进行运算。
强制类型转换
自动类型转换

变量作用域

类变量
实例变量
局部变量

public class Variable{
	Static int allclicks=0;//类变量
	String str="hello world";//实例变量
public void method(){
	int i =0;//局部变量
	}
}

类变量

/**
 * @BelongsProject: javatest
 * @BelongsPackage: PACKAGE_NAME
 * @Author: mayuenan
 * @CreateTime: 2022-10-04  14:54
 * @Description: TODO
 * @Version: 1.0
 */


public class Demo01 {

//    类变量
    static double salary =2500;

//属性:变量

//    实例变量:从属于对象,如果不自行初始化,这个类型的默认值 0 0.0
//    布尔值:默认值false
//    除了基本类型,其余的默认值都是null
    String name;
    int age;
    public static void main(String[] args) {
//        局部变量:必须声明和初始化值
        int i=10;
        System.out.println(i);

//        实例变量
//        变量类型 变量名字 = new  Demo01
        Demo01 demo01 = new Demo01();
        System.out.println(demo01.age);
        System.out.println(demo01.name);

//        类变量 static
        System.out.println(salary);

    }
}

常量

常量(Constant):初始化(initialize)后不能再改变值!不会变动的值。
所谓 常量可以理解成一种特殊的变量,它的值被设定后,在程序运行过程中不允许被改变。

final 常量名=;
final double PI=3.14;

常量名一般使用大写字符。

变量的命名规范

所有变量、方法、类名:见名知意
类成员变量:首字母小写和驼峰原则: monthSalary 除了第一个单词以外,后面的单词首字母大写 lastname lastName
局部变量:首字母小写和驼峰原则
常量:大写字母和下划线:MAX_VALUE
类名:首字母大写和驼峰原则: Man, GoodMan
方法名:首字母小写和驼峰原则: run(). runRuno

运算符

Java语言支持如下运算符:优先级()
算术运算符: +,-,,l,%,++, –
赋值运算符=
关系运算符:>,<,>=,<=,==,!=instanceof
逻辑运算符: &&,|l,!
位运算符: &,|,^,~,>>,<<,>>>(了解!!! )
条件运算符?∶
扩展赋值运算符:+=,-=,
=,/=

package operator;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: operator
 * @Author: mayuenan
 * @CreateTime: 2022-10-06  13:52
 * @Description: TODO
 * @Version: 1.0
 */
public class Demo01 {

    public static void main(String[] args) {
//        二元运算
        int a=10;
        int b=20;
        int c=25;
        int d=25;

        System.out.println(a+b);
        System.out.println(a-b);
        System.out.println(a*b);
        System.out.println(a/(double)b);
    }
}

package operator;

public class Demo02 {

    public static void main(String[] args) {
        long a =1l;
        int b =123;
        short c=10;
        byte d =8;

        System.out.println(a+b+c+d);//Long
        System.out.println(b+c+d);//Int
        System.out.println(c+d);//Int
    }
}

package operator;

public class Demo03 {

    public static void main(String[] args) {
//        关系运算符返回的结果:正确,错误 布尔值
//        if
        int a = 10;
        int b = 20;
        int c = 22;

        System.out.println(c%a);

        System.out.println(a>b);
        System.out.println(a<b);
        System.out.println(a==b);
        System.out.println(a!=b);

    }
}

package operator;

public class Demo04 {

    public static void main(String[] args) {
//        ++ -- 自增,自减 一元运算符
        int a = 3;

        int b = a++;//a++ a = a+1
//        执行完这行代码后,先给b赋值,在自增
        int c = ++a;//执行完这行代码前,先自增,在给B赋值


        System.out.println(a);
        System.out.println(b);
        System.out.println(c);

//        幂运算 2^3 2*2*2 = 8
//        很多运算,我们会使用一些工具类来操作
        double pow = Math.pow(2, 3);
        System.out.println(pow);
    }
}

package operator;

//逻辑运算符
public class Demo05 {
    public static void main(String[] args) {

//        与(and) 或(or) 非 (取反)
        boolean a =true;
        boolean b =false;

        System.out.println("a && b:" + (a&&b));//逻辑与运算:两个变量都为真,结果才为true
        System.out.println("a || b:" + (a||b));//逻辑或运算:两个变量有一个为真,结果才为true
        System.out.println("!(a && b):" + !(a&&b));//如果是真,则变为假,如果是假则变为真

//        短路运算
        int c =5;
        boolean d = (c<4) && (c++ <4);

        System.out.println(d);
        System.out.println(c);

    }
}

package operator;

public class Demo06 {
    public static void main(String[] args) {
        /*A = 0011 1100
        * B = 0000 1101
        * A&B = 0000 1100
        * A|B = 0011 1101
        * A^B = 0011 0001
        * ~B  = 1111 0010
        *
        * 2*8 = 2*2*2*2
        *
        * 左移<< *2,把数字乘2
        * 右移>> /2,把数字除以2
        * 0000 0000 0
        * 0000 0001 1
        * 0000 0010 2
        *
        * */

        System.out.println(2<<3);
    }
}

package operator;

public class Demo07 {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;

        a+=b; // a = a+b
        a-=b;//a = a-b

        System.out.println(a);

//        字符串连接符  + ,String
        System.out.println(a+b);
        System.out.println("" + a+b);
        System.out.println( a+b + "");

    }
}

package operator;

//三元运算符
public class Demo08 {
    public static void main(String[] args) {
        //x ? y : z
        //如果 x== true ,则 结果为 y,否则结果为 z

        int score =80;
        String type = score <60 ? "不及格":"及格";
        System.out.println(type);;
    }
}

包机制

为了更好地组织类,Java提供了包机制,用于区别类名的命名空间。
包语句的语法格式为:
package pkg1[. pkg2[. pkg3…]];
一般利用公司域名倒置作为包名;
为了能够使用某一个包的成员,我们需要在Java程序中明确导入该包。使用"import”语句可完成此功能
import package1[.package2…].(classname|*);

JavaDoc

javadoc命令是用来生成自己API文档的
参数信息
@author 作者名
@version版本号
@since指明需要最早使用的jdk版本
@param参数名
@return返回值情况
@throws 异常抛出情况

![image.png](https://img-blog.csdnimg.cn/img_convert/52977bc22de1c0b89ddd5c0794c77031.png#averageHue=#686664&clientId=u3c6a5d5b-9e0f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=417&id=ue8ef8153&margin=[object Object]&name=image.png&originHeight=417&originWidth=1124&originalType=binary&ratio=1&rotation=0&showTitle=false&size=88200&status=done&style=none&taskId=uc4f376d9-d64c-4093-9db7-5be29a4c343&title=&width=1124)
javadoc -encoding UTF-8 -charset UTF-8 Doc.java

package base;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: base
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  01:46
 * @Description: TODO
 * @Version: 1.0
 * @since 1.8
 */
public class Doc {
    String name;

    /**
     * @description:
     * @author: mayuenan
     * @date: 2022/10/7 1:49
     * @param: [name]
     * @return: java.lang.String
     * @throws Exception
     **/
    public String test(String name)throws Exception{
        return name;
    }
//    作业:使用IDEA产生JavaDoc 文档
//    https://www.cnblogs.com/hyxdz/p/15958937.html
    
}

Java流程控制

1、用户交互Scanner

Scanner对象
之前我们学的基本语法中我们并没有实现程序和人的交互,但是Java给我们提供了这样一个工具类,我们可以获取用户的输入。java.util.Scanner 是Java5的新特征,我们可以通过Scanner类来获取用户的输入

基本语法:

Scanner s = new Scanner(System. in);
通过Scanner类的next()与nextLine()方法获取输入的字符串,在读取前我们一-般需要使用hasNext(与hasNextLine()判断是否还有输入的数据。

package scanner;

import java.util.Scanner;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: scanner
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  02:13
 * @Description: TODO
 * @Version: 1.0
 */
public class Demo01 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("使用next方式接收:");

//        判断用户有没有输入原字符串
        if (scanner.hasNext()){
            String str = scanner.next();
            System.out.println("输入的字符串是" + str);
        }
//        凡是属于IO流的类如果不关闭会一直占用资源,要养成好习惯用完就关掉
        scanner.close();

    }
}

Scanner对象

next():
◆1.一定要读取到有效字符后才可以结束输入。
◆2.对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
◆3.只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
◆4. next()不能得到带有空格的字符串。
nextLine():
◆1.以Enter为结束符,也就是说nextLine()方法返回的是输入回车之前的所有字符。
◆2.可以获得空白。

package scanner;

import java.util.Scanner;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: scanner
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  02:13
 * @Description: TODO
 * @Version: 1.0
 */
public class Demo01 {
    public static void main(String[] args) {

//        创建一个扫描器对象,用于接收键盘数据
        Scanner scanner = new Scanner(System.in);
        System.out.println("使用next方式接收:");

//        判断用户有没有输入字符串
        if (scanner.hasNext()){
//            使用next方式接收
            String str = scanner.next();
            System.out.println("输入的字符串是" + str);
        }
//        凡是属于IO流的类如果不关闭会一直占用资源,要养成好习惯用完就关掉
        scanner.close();

    }
}

package scanner;

import java.util.Scanner;

public class Demo04 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

//        从键盘接收数据
        int i=0;
        float f = 0.0f;

        System.out.println("请输入整数:");

//        如果。。。那么。。。
        if (scanner.hasNextInt()) {
            i = scanner.nextInt();
            System.out.println("整数数据:" +i);
        }else {
            System.out.println("输入的不是整数数据!");
        }


        System.out.println("请输入小数:");
        //        如果。。。那么。。。
        if (scanner.hasNextFloat()) {
            f = scanner.nextFloat();
            System.out.println("浮点数据:" +f);
        }else {
            System.out.println("输入的不是整数数据!");
        }

        scanner.close();
    }
}

package scanner;

import java.util.Scanner;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: scanner
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  09:15
 * @Description: TODO
 * @Version: 1.0
 */
public class Demo05 {
    public static void main(String[] args) {
//        我们可以输入多个数字,并求其总和与平均数,每输入一个数字用回车确认,通过输入非数字结束输入并输出执行结果:

        Scanner scanner = new Scanner(System.in);
//        和
        double sum =0;

//        计算输入了多少个数字
        int m =0;

//        通过循环判断是否还有输入,并在里面对每一次进行求和统计
        while (scanner.hasNextDouble()){
            double x = scanner.nextDouble();
            m =m+1;//m++
            sum = sum + x;
            System.out.println("你输入了第"+ m + "个数据 然后当前结果sum = "+ sum);
        }
        System.out.println(m+ "个数的和为" + sum);
        System.out.println(m+ "个数的平均值为" + (sum/m));



        scanner.close();

    }
}

2、顺序结构

JAVA的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句执行。顺序结构是最简单的算法结构。
语句与语句之间,框与框之间是按从上到下的顺序进行的,它是由若干个依次执行的处理步骤组成的,它是任何一个算法都离不开的一种基本算法结构。

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  09:39
 * @Description: TODO
 * @Version: 1.0
 */
public class ShunXuDemo {
    public static void main(String[] args) {
        System.out.println("hello01");
        System.out.println("hello02");
        System.out.println("hello03");
        System.out.println("hello04");
        System.out.println("hello05");
    }
}

3、选择结构

if单选择结构

我们很多时候需要去判断一个东西是否可行,然后我们才去执行,这样一个过程在程序中用if语句来表示
语法:
if(布尔表达式){
布尔表达式
//如果布尔表达式为true将执行的语句
}
![image.png](https://img-blog.csdnimg.cn/img_convert/a91a2d35885211a8cdc8b5f80d4f00fc.png#averageHue=#f7f7f7&clientId=u767e3595-42ea-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=271&id=u22343537&margin=[object Object]&name=image.png&originHeight=271&originWidth=165&originalType=binary&ratio=1&rotation=0&showTitle=false&size=20397&status=done&style=none&taskId=u9be1b026-6bc3-4f16-8d20-23cb63eca13&title=&width=165)

package struct;

import java.util.Scanner;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  09:45
 * @Description: TODO
 * @Version: 1.0
 */
public class IfDemo01 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.println("请输入内容: ");
        String s = scanner.nextLine();

//        equals:判断字符串是否相等
        if (s.equals("Hello")){
            System.out.println(s);
        }
        System.out.println("End");


        scanner.close();

    }
}

if双选择结构

那现在有个需求,公司要收购一个软件,成功了,给人支付100万元,失败了,自己找人开发。这样的需求用一个if就搞不定了,我们需要有两个判断,需要一个双选择结构,所以就有了if-else结构。
语法:
if(布尔表达式){
//如果布尔表达式的值为true
}else{
//如果布尔表达式的值为false
}
![image.png](https://img-blog.csdnimg.cn/img_convert/c20d5c5036087802978f398be00b8eaa.png#averageHue=#f6f6f6&clientId=u767e3595-42ea-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=199&id=ued2eb92f&margin=[object Object]&name=image.png&originHeight=199&originWidth=186&originalType=binary&ratio=1&rotation=0&showTitle=false&size=18511&status=done&style=none&taskId=u3ec0f87f-02ce-466e-a646-11223a009ec&title=&width=186)

package struct;

import java.util.Scanner;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  12:25
 * @Description: TODO
 * @Version: 1.0
 */
public class IfDemo02 {
    public static void main(String[] args) {
//    考试分数大于60就是及格,小于60分就不及格
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入成绩: ");
        int score = scanner.nextInt();

        if (score>60){
            System.out.println("及格");
        }else {
            System.out.println("不及格");
        }
        scanner.close();
    }
}

if多选择结构

我们发现刚才的代码不符合实际情况,真实的情况还可能存在ABCD,存在区间多级判断。比如90-100就是A,80-90就是B…等等,在生活中我们很多时候的选择也不仅仅只有两个,所以我们需要一个多选择结构来处理这类问题!
语法:
if(布尔表达式1){
//如果布尔表达式1的值为true执行代码
}else if(布尔表达式2){
//如果布尔表达式2的值为true执行代码
}else if(布尔表达式3){
//如果布尔表达式3的值为true执行代码
}else {
//如果以上布尔表达式都不为true执行代码
}
![image.png](https://img-blog.csdnimg.cn/img_convert/7feda57cf4bcd2ed24cd7983f5785c36.png#averageHue=#c083c7&clientId=u767e3595-42ea-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=206&id=u673cd1d4&margin=[object Object]&name=image.png&originHeight=206&originWidth=261&originalType=binary&ratio=1&rotation=0&showTitle=false&size=32610&status=done&style=none&taskId=u2bfeb44c-2756-4ed7-8003-9ba7f10e0b1&title=&width=261)

package struct;

import java.util.Scanner;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  12:34
 * @Description: TODO
 * @Version: 1.0
 */
public class IfDemo03 {
    public static void main(String[] args) {
//    考试分数大于60就是及格,小于60分就不及格
        Scanner scanner = new Scanner(System.in);

        /*
         if 语句至多有1个else语句,else 语句在所有的 else if 之后
         if 语句可以有若干个 else if 语句,它们必须在 else语句之前
         一旦其中一个else if 语句检测为 true , 其他的 else if 以及 else 语句都将跳过执行
         */
        System.out.println("请输入成绩: ");
        int score = scanner.nextInt();

        if (score==100){
            System.out.println("恭喜满分");
        }else if (score <100 && score >= 90){
            System .out.println("A级");
        }else if (score <90 && score >= 80){
            System .out.println("B级");
        }else if (score <80 && score >= 70){
            System .out.println("C级");
        }else if (score <70 && score >= 60){
            System .out.println("D级");
        }else if (score <60 && score >= 0){
            System .out.println("不及格");
        }else {
            System.out.println("成绩不合格");
        }
        scanner.close();
    }
}

嵌套的if结构

使用嵌套的if…else语句是合法的。也就是说你可以在另一个if或者else if语句中使用if或者else if 语句。你可以像if语句一样嵌套else if…else.
语法:
if(布尔表达式1){
//如果布尔表达式1的值为true执行代码
if(布尔表达式2){
//如果布尔表达式2的值为true执行代码)
}
}
思考?我们需要寻找一个数。在1-100之间

switch多选择结构

多选择结构还有一个实现方式就是switch case语句。
switch case语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
switch语句中的变量类型可以是:
byte、short、 int或者char.
Java SE 7开始
switch支持字符串String类型了
同时case标签必须为字符串常量或字面量。

switch(expression){
case value :
//语句
break; //可选
case value :
//语句
break; //可选
//你可以有任意数量的case语句
default : //可选
//语句
}

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  19:02
 * @Description: TODO
 * @Version: 1.0
 */
public class SwitchDemo01 {
    public static void main(String[] args) {
// case 穿透 // switch 匹配一个具体的值
        char grade = 'F';

        switch (grade){
            case 'A':
                System.out.println("优秀");
                break;//可选
            case 'B':
                System.out.println("良好");
                break;//可选
            case 'C':
                System.out.println("及格");
                break;//可选
            case 'D':
                System.out.println("再接再厉");
                break;//可选
            case 'E':
                System.out.println("挂科");
                break;//可选
            default:
                System.out.println("未知等级");
        }
    }
}

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  19:10
 * @Description: TODO
 * @Version: 1.0
 */
public class SwitchDemo02 {
    public static void main(String[] args) {

//        JDK的新特性,表达式结果可以是字符串
//        字符的本质还是数字

//        反编译 java--class(字节码文件)--反编译 (IDEA)
        String name = "小马";
        switch (name){
        case "小王":
        System.out.println("小王");
        break;
        case "小马":
        System.out.println("小马");
        break;
        default:
            System.out.println("姓啥呀!");
    }
    }
}

4、循环结构

while循环

while是最基本的循环,它的结构为:
while(布尔表达式) {
//循环内容
}
只要布尔表达式为true,循环就会一直执行下去。
我们大多数情况是会让循环停止下来的,我们需要一个让表达式失效的方式来结束循环。
少部分情况需要循环一直执行,比如服务器的请求响应监听等。
循环条件一直为true就会造成无限循环【死循环】,我们正常的业务编程中应该尽量避免死循环。会影响程序性能或者造成程序卡死奔溃!
思考:计算1+2+3+…+100=?

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  19:38
 * @Description: TODO
 * @Version: 1.0
 */
public class WhileDemo01 {
    public static void main(String[] args) {
//        输出 1~100
        int i= 0;
        while(i<100){
            i++;
            System.out.println(i);
        }
    }
}

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  19:41
 * @Description: TODO
 * @Version: 1.0
 */
public class WhileDemo02 {
    public static void main(String[] args) {
//        死循环
        while (true){
//            等待客户端连接
//            定时检查任务
//            。。。。。。
        }
    }
}

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  19:43
 * @Description: TODO
 * @Version: 1.0
 */
public class WhileDemo03 {
    public static void main(String[] args) {
//        计算1+2+3+...+100=?
//        高斯的故事
        int i=0;
        int sum = 0;
        while (i<=100){
            sum = sum +i;
            i++;
        }
        System.out.println(sum);
    }
}

do…while循环

对于while语句而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次。
do…while循环和while循环相似,不同的是,do…while循环至少会执行一次。
do {
//代码语句
}while(布尔表达式);
While和do-While的区别:
while先判断后执行。dowhile是先执行后判断!
Do…while总是保证循环体会被至少执行一次!这是他们的主要差别。

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  19:48
 * @Description: TODO
 * @Version: 1.0
 */
public class DoWhileDemo01 {
    public static void main(String[] args) {
        int i=0;
        int sum = 0;

        do {
            sum = sum +i;
            i++;
        }while (i<=100);
        System.out.println(sum);
    }
}

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  19:50
 * @Description: TODO
 * @Version: 1.0
 */
public class DoWhileDemo02 {
    public static void main(String[] args) {
        int a =0;
        while (a<0){
            System.out.println(a);
            a++;
        }
        System.out.println("================");
        do {
            System.out.println(a);
            a++;
        }while (a<0);
    }
}

报错整合:Error:java: Compilation failed: internal java compiler error 解决办法

https://blog.csdn.net/ruoxiyun/article/details/88256928

for循环

虽然所有循环结构都可以用while或者do….while表示,但Java提供了另一种语句——for循环,使一些循环结构变得更加简单。
for循环语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构。
for循环执行的次数是在执行前就确定的。语法格式如下:
for(初始化;布尔表达式;更新){
//代码语句
}
练习1:计算0到100之间的奇数和偶数的和
练习2:用while或for循环输出1-1000之间能被5整除的数,并且每行输出3个
练习3:打印九九乘法表

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  20:12
 * @Description: TODO
 * @Version: 1.0
 */
public class ForDemo02 {
    public static void main(String[] args) {
//        练习1:计算0到100之间的奇数和偶数的和
        int oddSum =0;
        int evenSum = 0;

        for (int i = 0; i <= 100; i++) {
            if(i%2!=0){//奇数
                oddSum +=i;
            }else{
                evenSum +=i;//偶数
            }
        }
        System.out.println("奇数的和"+oddSum);
        System.out.println("偶数的和"+evenSum);

    }
}

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  20:17
 * @Description: TODO
 * @Version: 1.0
 */
public class ForDemo03 {
    public static void main(String[] args) {
//        练习2:用while或for循环输出1-1000之间能被5整除的数,并且每行输出3个
        for (int i = 0; i <= 1000; i++) {
            if(i%5 ==0){
                System.out.print(i+"\t");
            }
            if (i%(5*3)==0){//每行
                System.out.println();
            }
        }
//        println 输出完会换行
//        print 输出完不会换行
    }
}

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  20:21
 * @Description: TODO
 * @Version: 1.0
 */

/*
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6  3×3=9
1×4=4 2×4=8  3×4=12 4×4=16
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 8×8=64
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81
* */
public class ForDemo04 {
    public static void main(String[] args) {
//        练习3:打印九九乘法表

//        1、我们先打印第一列
//        2、把固定的1 再用一个循环包起来
//        3、去掉重复项,i<j
//        4、调整样式
        for (int j = 1; j < 9; j++) {
            for (int i=1;i<=j;i++){
                System.out.print(j+"*" +i+"="+(j*i) +"\t");
            }
            System.out.println();
        }
    }
}

在Java5中引入了一种主要用于数组的增强型for循环。

增强for循环

这里我们先只是见一面,做个了解,之后数组我们重点使用
Java5 引入了一种主要用于数组或集合的增强型for循环。
Java增强for循环语法格式如下:
for(声明语句∶表达式)
//代码句子
}
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
表达式:表达式是要访问的数组名,或者是返回值为数组的方法。

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  20:34
 * @Description: TODO
 * @Version: 1.0
 */
public class ForDemo05 {
    public static void main(String[] args) {
        int[] numbers = {10,20,30,40,50};//定义一个数组
        for(int i=0;i<5;i++){
            System.out.println(numbers[i]);
        }
        System.out.println("==================");

//        遍历数组的元素
        for (int x:numbers) {
            System.out.println(x);
        }
    }
}

5、break & continue

break在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行退出循环,不执行循环中剩余的语句。(break语句也在switch语句中使用)
continue语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
关于goto关键字
goto关键字很早就在程序设计语言中出现。尽管goto仍是Java的一个保留字,但并未在语言中得到正式使用;Java没有goto。然而,在break和continue这两个关键字的身上,我们仍然能看出一些goto的影子—带标签的break和continue.
“标签”是指后面跟一个冒号的标识符,例如: label:
对Java来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一个循环,由于break和continue关键字通常只中断当前循环,但若随同标签使用,它们就会中断到存在标签的地方。

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  20:44
 * @Description: TODO
 * @Version: 1.0
 */
public class BerakDemo01 {
    public static void main(String[] args) {
        int i =0;
        while (i<=100){
            i++;
            System.out.println(i);
            if (i==30){
                break;
            }
        }
        System.out.println("123");
    }
}

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  20:46
 * @Description: TODO
 * @Version: 1.0
 */
public class ContinueDemo01 {
    public static void main(String[] args) {
        int i=0;
        while(i<100){
            i++;
            if(i%10==0){
                System.out.println();
                continue;
            }
            System.out.print(i);
        }
    }
}

package struct;

/**
 * @BelongsProject: javatest
 * @BelongsPackage: struct
 * @Author: mayuenan
 * @CreateTime: 2022-10-07  20:57
 * @Description: TODO
 * @Version: 1.0
 */
public class LabelDemo01 {
    public static void main(String[] args) {
//        打 印e1-150.之网所有的质数
//        质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。

//        了解就好,不建议使用
        int count = 0;
        outer:for (int i=101;i<150;i++){
            for (int j = 2; j<i/2;j++){
                if(i%j ==0){
                    continue outer;
                }
            }
            System.out.print(i+"\t");
        }
    }
}

6、练习

方法

Java方法详解

1、何谓方法

System.out.println(),那么它是什么呢?//输出语句 system类out 对象 println方法
Java方法是语句的集合,它们在一起执行一个功能。
◆方法是解决一类问题的步骤的有序组合
◆方法包含于类或对象中
◆方法在程序中被创建, 在其他地方被引用
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的
时候,最好保持方法的原子性就是一个方法只完成1个功能,这样利于我们后期的扩展。
回顾:方法的命名规则?//首字母小写驼峰命名

2、方法的定义及调用

Java的方法类似于其它语言的函数,是一段用来完成特定功能的代码片段,一般情况下,定义一个方法包含以下语法:

方法包含一个方法头和一个方法体。

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

方法调用

◆调用方法:对象名.方法名(实参列表)
◆Java支持两种调用方法的方式,根据方法是否返回值来选择。
◆当方法返回一个值的时候,方法调用通常被当做一个值。 例如:
int larger = max(30,40);
◆如果方法返回值是void,方法调用一定是一条语句。
System. out . println(Hello, kuangshen!");
◆课后拓展了解:值传递(Java)和引用传递

3、方法重载

◆重载就是在一个类中,有相同的函数名称,但形参不同的函数。
◆方法的重载的规则:
方法名称必须相同
参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等)。
◆方法的返回类型可以相同也可以不相同。
◆仅仅返回类型不同不足以成为方法的重载。
◆实现理论:
◆方法名称相同时,编译器会根据调用方法的参数个数、参数类型等去逐个匹配,以选择对
应的方法,如果匹配失败,则编译器报错。

4、命令行传参

有时候你希望运行一个程序时候再传递给它消息。这要靠传递命令行参数给main()函数实现。
public class CommandLine {
public static void main( String args[]){
for(int i=o; i<args.length; i++){
system.out.println( “args[” +i + “]:” + args[i]);}
}
}
![image.png](https://img-blog.csdnimg.cn/img_convert/53a1ad55b51fa02dbd46a1d2c48c9230.png#averageHue=#4c4640&clientId=udf17f820-ea04-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=670&id=uf69cce9b&margin=[object Object]&name=image.png&originHeight=670&originWidth=1258&originalType=binary&ratio=1&rotation=0&showTitle=false&size=531530&status=done&style=none&taskId=u0fd740f5-48c7-4d58-8b6b-0b95fa88d49&title=&width=1258)

5、可变参数

JDK 1.5开始,Java支持传递同类型的可变参数给一个方法。
在方法声明中,在指定参数类型后加一个省略号(.…).
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
public static void printMax( double. . . numbers) {
if ( numbers.length == 0) {
system.out.println( “No argument passed” );return;
}
double result = numbers[0];
//排序!
for (int i = 1; i <numbers.length; i++){
if (numbers[i] >result) {
result = numbers[i];
}
}
System.out.println( "The max value is " + result);
}

package method;

/**
* @title: Demo04
* @Author yuenan.ma
* @Date: 2022/10/16 9:25
* @Version 1.0
*/
public class Demo04 {
    public static void main(String[] args) {
        //        Demo04 demo04 = new Demo04();
        //        demo04.test(1,2,3,4,5);

        printMax(34,3,3,2,56.5);
        printMax(new double[]{1,2,3});
    }
    /*    public void method(){}
public void method(int i){}
public void method(int i,int i2){}
public void method(int i,double i2){}*/
    /* public void test(int... i){
System.out.println(i[0]);
System.out.println(i[1]);
System.out.println(i[2]);
System.out.println(i[3]);
System.out.println(i[4]);
}*/
    /*    public void test(int i1,int... i){//可变传参数必须在最后面,相同类型
System.out.println(i[0]);
System.out.println(i[1]);
System.out.println(i[2]);
System.out.println(i[3]);
System.out.println(i[4]);
}*/

    //    简单排序算法
    public static void printMax(double... numbers){
        if (numbers.length == 0){
            System.out.println("No argment passed");//没有传参数
            return;
        }
        double result = numbers[0];

        //排序!
        for(int i=0;i<numbers.length;i++){
            if (numbers[i] > result){
                result = numbers[i];
            }
        }
        System.out.println("The max value is " + result);
    }
}

6、递归(面试高频稳点)

A方法调用B方法,我们很容易理解!
递归就是:A方法调用A方法!就是自己调用自己

利用递归可以用简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。
递归结构包括两个部分:
递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。
递归体:什么时候需要调用自身方法。

![111.png](https://img-blog.csdnimg.cn/img_convert/ca2dc49a59a662d92d254236b048a85a.png#averageHue=#f8f8f8&clientId=u816b9760-e280-4&crop=0&crop=0&crop=1&crop=1&from=drop&id=u0d5e7faf&margin=[object Object]&name=111.png&originHeight=222&originWidth=576&originalType=binary&ratio=1&rotation=0&showTitle=false&size=3170&status=done&style=none&taskId=u4225d957-e544-4a4f-a375-c1180e10fce&title=)
递归标志

![image.png](https://img-blog.csdnimg.cn/img_convert/2ac9a72c184212daceaa20f6c297d5ea.png#averageHue=#faf9f6&clientId=u7a5de819-b7c5-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=172&id=u005cb95c&margin=[object Object]&name=image.png&originHeight=172&originWidth=663&originalType=binary&ratio=1&rotation=0&showTitle=false&size=11471&status=done&style=none&taskId=ub007663f-5dc1-4b7a-8e59-679b32574c8&title=&width=663)

package method;

/**
* @title: Demo05
* @Author yuenan.ma
* @Date: 2022/10/16 10:33
* @Version 1.0
*/
public class Demo05 {
    public static void main(String[] args) {
        Demo05 demo05 = new Demo05();
        demo05.test();
    }

    //        Exception in thread "main" java.lang.StackOverflowError 栈溢出异常
    public void test(){
        test();
    }
}

边界条件:边界
前阶段:
返回阶段
JAVA都是使用栈机制

package method;

import java.util.Scanner;

/**
 * @title: Demo06
 * @Author yuenan.ma
 * @Date: 2022/10/16 10:38
 * @Version 1.0
 *
 * 2!  2*1
 * 3!  3*2*1
 * 4!  4*3*2*1
 * 5!  5*4*3*2*1
 */
public class Demo06 {
    public static void main(String[] args) {
//        System.out.println(f(3));

//        写一个计算器,要求实现加减乘除功能,并且能够循环接收新的数据,通过用户交

        /*
        *    思路推荐:
            写4个方法:加减乘除
            利用循环+switch进行用户交互传递需要操作的两个数
            输出结果
          */

        while (true){
            System.out.println("1、加法运算");
            System.out.println("2、减法运算");
            System.out.println("3、乘法运算");
            System.out.println("4、除法运算");
            System.out.println("请输入运算功能的序号");
            Scanner scanner = new Scanner(System.in);//创建一个扫描器对象,从键盘接收数据
            int x =scanner.nextInt();//输入一个数
            switch (x){
                case 1:
                    System.out.println("您选择的是加法运算");
                    System.out.println("请输入第一个数: ");
                    addition();//如果是1,那就调用add()方法
                    break;
                case 2:
                    System.out.println("您选择的是减法运算");
                    System.out.println("请输入第一个数: ");
                    subtraction();//如果是2,那就调用add()方法
                    break;
                case 3:
                    System.out.println("您选择的是乘法运算");
                    System.out.println("请输入第一个数: ");
                    multipcation();//如果是3,那就调用add()方法
                    break;
                case 4:
                    System.out.println("您选择的是除法运算");
                    System.out.println("请输入第一个数: ");
                    division();//如果是4,那就调用add()方法
                    break;
                default:
                    System.out.println("您的输入错误,请重新输入: ");

            }
            System.out.println("是否继续?继续请按1,结束请按2");
            int i = scanner.nextInt();
            if (i==1){
                System.out.println("再来一个!");
                continue;
            }if(i==2){
                System.out.println("谢谢使用!");
                break;
            }
        }
    }

    private static void division() {//除
        Scanner scanner = new Scanner(System.in);
        int i = scanner.nextInt();
        System.out.println("请输入第二个数: ");
        int i1 = scanner.nextInt();
        System.out.println("两数相除结果为: " +(i/i1));

    }

    private static void multipcation() {//乘
        Scanner scanner = new Scanner(System.in);
        int i = scanner.nextInt();
        System.out.println("请输入第二个数: ");
        int i1 = scanner.nextInt();
        System.out.println("两数相乘结果为: " +(i*i1));

    }

    private static void subtraction() {//减
        Scanner scanner = new Scanner(System.in);
        int i = scanner.nextInt();
        System.out.println("请输入第二个数: ");
        int i1 = scanner.nextInt();
        System.out.println("两数相减结果为: " +(i-i1));
    }

    private static void addition() {//加法
        Scanner scanner = new Scanner(System.in);
        int i = scanner.nextInt();
        System.out.println("请输入第二个数: ");
        int i1 = scanner.nextInt();
        System.out.println("两数相加结果为: " +(i+i1));
    }
//
//    public static int f(int n){
//        if(n ==1){
//            return 1;
//        }else {
//            return n*f(n-1);
//        }
//    }

//    加法

}

数组

1、数组概述,最简单的数据结构

数组的定义
数组是相同类型数据的有序集合.
数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们.

2、数组声明创建

首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:
dataType[ ] arrayRefVar;//首选的方法

dataType arrayRefVar[];//效果相同,但不是首选方法
Java语言使用new操作符来创建数组,语法如下:
dataType[] arrayRefVar = new dataType[ arraysize];
数组的元素是通过索引访问的,数组索引从0开始。
获取数组长度: arrays.length
![image.png](https://img-blog.csdnimg.cn/img_convert/3a8c26e92e14711c24a149335eeb3c56.png#averageHue=#f1f1f1&clientId=u7a5de819-b7c5-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=531&id=u6e8ce33a&margin=[object Object]&name=image.png&originHeight=531&originWidth=1381&originalType=binary&ratio=1&rotation=0&showTitle=false&size=7333&status=done&style=none&taskId=u9583e9ff-c9ef-4a05-8437-10c14151773&title=&width=1381)

package array;

/**
 * @title: ArrayDemo01
 * @Author yuenan.ma
 * @Date: 2022/10/20 19:48
 * @Version 1.0
 */
public class ArrayDemo01 {
    // 变量的类型 变量名称 = 变量的值;
    public static void main(String[] args) {
        int[] nums;//1、声明一个数组,在类型后面加[],代表数组
//        int nums1[];//1、定义,C和C++程序员快速的掌握JAVA

        nums = new int[10];//这里面可以存放10个int类型的数字

//        数组元素中赋值
        nums[0] = 10;
        nums[1] = 1;
        nums[2] = 2;
        nums[3] = 3;
        nums[4] = 4;
        nums[5] = 5;
        nums[6] = 6;
        nums[7] = 7;
        nums[8] = 8;
        nums[9] = 9;
//        ArrayIndexOutOfBoundsException 数组下标越界
//        System.out.println(nums[10]);

        //计算所有元素的和,arrays.length
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        System.out.println("数组的和为: " +sum);
    }
}

内存分析



三种初始化
静态初始化
int[] a = {1,2,3};
Man[ ].mans = {new Man(1,1),new Man(2,2)};
动态初始化
int[] a = new int[2];
a[0]=1;
a[1]=2;
数组的默认初始化
数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。

package array;

/**
 * @title: ArrayDemo02
 * @Author yuenan.ma
 * @Date: 2022/10/22 11:22
 * @Version 1.0
 */
public class ArrayDemo02 {
    public static void main(String[] args) {
//        静态初始化 创建+赋值
        int[] a = {1,2,3,4,5,6,7,8,9};
        System.out.println(a[0]);
//        Man[] mans = {new Man(),new Man()};

//        动态初始化
        int[] b = new int[10];
        b[0]=10;
        System.out.println(b[0]);
        System.out.println(b[1]);//没有被初始化的初始值为0
        
    }
}

数组的四个基本特点
其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
其元素必须是相同类型,不允许出现混合类型。
数组中的元素可以是任何数据类型,包括基本类型和引用类型。
数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的
数组边界
下标的合法区间:[0, length-1],如果越界就会报错;
public static void main(String[ ] args) {
int[] a=new int[2];
system.out.println(a[2]);
}
ArraylndexOutOfBoundsException:数组下标越界异常!
小结:
数组是相同数据类型(数据类型可以为任意类型)的有序集合数组也是对象。数组元素相当于对象的成员变量
数组长度的确定的,不可变的。如果越界,则报:ArrayIndexOutofBounds

3、数组使用

For-Each循环
数组作方法入参
数组作返回值
package array;

/**
 * @title: ArrayDemo04
 * @Author yuenan.ma
 * @Date: 2022/10/22 11:44
 * @Version 1.0
 */
public class ArrayDemo04 {
    public static void main(String[] args) {
        int[] arrays = {1,2,3,4,5};
        for (int array : arrays) {
            System.out.println(array);
        }
        printArray(arrays);
        int[] reverse = reverse(arrays);
        printArray(reverse);
    }

//    打印数组元素
    public static void printArray(int[] arrays){
        for (int i = 0; i < arrays.length; i++) {
            System.out.print(arrays[i]+" ");
        }
    }

//    反转数组
    public static int[] reverse(int[] arrays){
        int[] result = new int[arrays.length];

//        反转操作
        for (int i = 0, j = result.length-1; i < arrays.length; i++,j--) {
            result[j] = arrays[i];
        }
        return result;
    }
}

4、多维数组

多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。
二维数组
int a[][] = new int[2][5];
解析:以上二维数组a可以看成一个两行五列的数组。
思考:多维数组的使用?
num[1] [0];

5、Arrays类

数组的工具类java.util.Arrays
由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。
查看JDK帮助文档
Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而"不用"使用对象来调用(注意:是"不用”而不是"不能")
具有以下常用功能:
给数组赋值:通过fill方法。
对数组排序:通过sort方法,按升序。
比较数组:通过equals 方法比较数组中元素值是否相等。
查找数组元素:通过binarySearch方法能对排序好的数组进行二分查找法操作。
冒泡排序
冒泡排序无疑是最为出名的排序算法之一,总共有八大排序

冒泡的代码还是相当简单的,两层循环,外层冒泡轮数,里层依次比较,江湖中人人尽皆知。我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为O(n2)。
思考:如何优化?

package array;

import java.util.Arrays;

/**
 * @title: ArrayDemo07
 * @Author yuenan.ma
 * @Date: 2022/10/23 16:43
 * @Version 1.0
 */
//    冒泡排序
//    1、比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换它们的位置
//    2、每一次比较,都会产生出一个最大,或最小的数
//    3、下一轮则可以少一次排序
//    4、依次循环,直到结束
public class ArrayDemo07 {
    public static void main(String[] args) {
        int[] a = {1,4,3,5,6,7,8,9,2};
        int[] sort = sort(a);//调用完我们自己写的排序方法以后,返回一个排序后的数组
        System.out.println(Arrays.toString(sort));
    }
    public static int[] sort(int[] array){
//        临时变量
        int temp = 0;
//        外层循环,判断我们这个要走多少次
        for (int i = 0; i < array.length-1; i++) {
//            内层循环,比较判断两个数,如果第一个数,比第二个数大,则交换位置

            boolean flag = false;//通过flag标志位减少没有意义的比较

            for (int j = 0; j < array.length-1-i; j++) {
                if (array[j+1]<array[j]){
                    temp  = array[j];
                    array[j] = array[j+1];
                    array[j+1]=temp;
                    flag = true;
                }
            }

            if (flag = false){
                break;
            }
        }
        return array;
    }

}

6、稀疏数组

需求:编写五子棋游戏中,有存盘退出和续上盘的功能。
![image.png](https://img-blog.csdnimg.cn/img_convert/15dc72c712fe7d142b8ab38c661e20f1.png#averageHue=#f7f6f5&clientId=u2b47bcf8-e638-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=321&id=ua84829ed&margin=[object Object]&name=image.png&originHeight=321&originWidth=1013&originalType=binary&ratio=1&rotation=0&showTitle=false&size=186511&status=done&style=none&taskId=udc76a748-53c7-4946-8235-330b33b6b84&title=&width=1013)
压缩算法
分析问题:因为该二维数组的很多值是默认值0,因此记录了很多没有意义的数据。
解决:稀疏数组
稀疏数组介绍
当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组。
稀疏数组的处理方式是:
记录数组一共有几行几列,有多少个不同值
把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模
如下图:左边是原始数组,右边是稀疏数组
![image.png](https://img-blog.csdnimg.cn/img_convert/7a3ef41fb9fda31be9b281cca8b4f23a.png#averageHue=#fbf9f9&clientId=u6a264e25-ceb9-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=427&id=u84282722&margin=[object Object]&name=image.png&originHeight=258&originWidth=387&originalType=binary&ratio=1&rotation=0&showTitle=false&size=60172&status=done&style=none&taskId=uac7724c5-52a3-42ae-9a21-e16abdacd31&title=&width=640)

package array;

/**
 * @title: ArrayDemo08
 * @Author yuenan.ma
 * @Date: 2022/10/23 17:25
 * @Version 1.0
 */
public class ArrayDemo08 {
    public static void main(String[] args) {
       //1、创建一个二维数组 11*11 0,表示没有棋子,1:黑棋   2:白棋
        int[][] array1 = new int[11][11];
        array1[1][2] =1;
        array1[2][3] =2;
//        输出原始的数组
        System.out.println("输出原始的数组");
        for (int[] ints : array1) {
            for (int anInt : ints) {
                System.out.print(anInt +"\t");
            }
            System.out.println();
        }

        System.out.println("========================");
//        转换为稀疏数组保存
//        获取有效值的个数
        int sum =0;
        for (int i = 0; i < 11; i++) {
            for (int j = 0; j < 11; j++) {
                if (array1[i][j] !=0){
                    sum++;
                }
            }
        }
        System.out.println("有效值的个数:" +sum);

//        2、创建一个稀疏数组的数组
        int[][] array2 = new int[sum+1][3];
        array2[0][0] = 11;
        array2[0][1] = 11;
        array2[0][2] = sum;

        //遍历二维数组,将非0的值,放在稀疏数组中
        int count =0;
        for (int i = 0; i < array1.length; i++) {
            for (int j = 0; j < array1[i].length; j++) {
                if(array1[i][j] !=0){
                    count ++;
                    array2[count][0] =i;
                    array2[count][1] =j;
                    array2[count][2] =array1[i][j];
                }
            }
        }

        //输出稀疏数组
        System.out.println("输出稀疏数组");
        for (int i = 0; i < array2.length; i++) {
            System.out.println(array2[i][0]+"\t"
            +array2[i][1]+"\t"
            +array2[i][2]+"\t");

        }
        System.out.println("========================");
        System.out.println("还原稀疏数组");
//        1、读取稀疏数组
        int[][] array3 =new int[array2[0][0]][array2[0][1]];

//        2、给其中的元素还原它的值
        for (int i = 1; i < array2.length; i++) {

            array3[array2[i][0]][array2[i][0]] = array2[i][2];
        }

//        3、打印
        //        输出还原的数组
        System.out.println("输出还原的数组");
        for (int[] ints : array1) {
            for (int anInt : ints) {
                System.out.print(anInt +"\t");
            }
            System.out.println();
        }
    }
}

记录有效的坐标,记录原始坐标的大小以及有效值,然后记录出每一个数组的坐标,然后变成新的数组;还原的话,根据稀疏数组内的信息,把有效的值还原到有效的个数

面向对象编程

Java的核心思想就是0OP

1、初识面向对象

面向过程&面向对象

面向过程思想
步骤清晰简单,第一步做什么,第二步做什么…
面对过程适合处理一些较为简单的问题
面向对象思想
物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。

什么是面向对象

面向对象编程(Object-Oriented Programming, OOP)
面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
抽象:编程思想!持续学习,茅塞顿开!多实践,多测试大脑中的想法,实践出真知~
三大特性:
封装
继承
多态
从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象
从代码运行角度考虑是先有类后有对象。类是对象的模板。

2、方法回顾和加深

方法的定义
修饰符
返回类型
break和return的区别:跳出switch,结束循环,和return的区别
方法名:注意规范就OK ,见名知意
参数列表:(参数类型,参数名)…可变长参数
异常抛出:疑问,后面讲解

package oop.Demo01;

import java.io.IOException;

/**
 * @title: Demo01
 * @Author yuenan.ma
 * @Date: 2022/10/29 13:39
 * @Version 1.0
 */

//Demo01类
public class Demo01 {

    //main 方法
    public static void main(String[] args) {

    }

    /*修饰符 返回值类型 方法名(...){
        //方法体
        return 返回值;
    }
    *
    * */

    //return 结束方法,返回一个结果
    public String sayHello(){
        return "hello,Word";
    }

    public int max(int a,int b){
        return a>b?a:b;
    }

    //数组下标越界 :Arrayindexoutofbounds
    public void readFile(String file)throws IOException{

    }
}

方法的调用
静态方法
非静态方法

package oop.Demo01;

/**
 * @title: Demo02
 * @Author yuenan.ma
 * @Date: 2022/10/29 15:24
 * @Version 1.0
 */
public class Demo02 {


    public static void main(String[] args) {
//        Students.say();


        //实例化这个类 new
        //对象类型 对象名 = 对象值
        Students students = new Students();
        students.say();

    }

    //和类一起加载的
    public static void a(){
//        b();
    }

    //类实例化之后才存在的
    private void b() {
    }
    //静态方法 static


    //非静态方法
}

形参和实参
值传递和引用传递

package oop.Demo01;

/**
 * @title: Demo05
 * @Author yuenan.ma
 * @Date: 2022/10/30 12:48
 * @Version 1.0
 */
public class Demo05 {
//    引用传递:对象,本身还是值传递

    //对象,内存!
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.name);//null

        Demo05.change(person);
        System.out.println(person.name);
    }
    public static void change(Person person){
        //person是一个对象:指向的--->Person person = new Person();这是一个具体的人,可以改变属性!
        person.name ="小马";
    }
}


//定义一个person类,有一个属性,name
class Person{
    String name;
}

this关键字

类与对象的关系

类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物.
动物、植物、手机、电脑…
Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
对象是抽象概念的具体实例
张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例。
能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念.
我们可以将这些思想转换为代码实现!

创建与初始化对象

使用new关键字创建对象
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下俩个特点:
1.必须和类的名字相同
2.必须没有返回类型,也不能写void
构造器必须要掌握

package oop.Demo02;

/**
* @title: Student
* @Author yuenan.ma
* @Date: 2022/10/30 13:24
* @Version 1.0
*/

//学生类
public class Student {
    //属性
    String name;
    int age;

    //方法
    public void study(){
        System.out.println(this.name+"在学习");
    }
}


//person-->身高,体重,年龄,国家,国籍
//学程序好?对世界进行更好的建模!--宅! 音乐,旅游




package oop.Demo02;

/**
 * @title: Application
 * @Author yuenan.ma
 * @Date: 2022/10/30 13:25
 * @Version 1.0
 */

//一个项目应该只存在一个main方法
public class Application {
    public static void main(String[] args) {

        //类:抽象的,实例化
        //类实例化会返回一个自己的对象!
        //student对象就是一个student类的具体实例!
        Student student = new Student();
        Student student2 = new Student();

        student.name = "小梦";
        student.age = 3;

        System.out.println(student.name);
        System.out.println(student.age);

        student2.name = "小红";
        student2.age = 3;
        System.out.println(student2.name);
        System.out.println(student2.age);

    }
}

package oop.Demo02;

/**
* @title: Person
* @Author yuenan.ma
* @Date: 2022/10/30 14:16
* @Version 1.0
*/

//java ---> class
public class Person {
    //一个类即使什么也不写,它也会存在一个方法
    //显示的定义一个构造器
    //默认构造器
    String name;

    //实例化初始值
    //1、使用 new关键字必须要有构造器
    //2、用来初始化值
    public Person(){
        this.name = "小马";
    }

    //重载
    //    有参构造器:一旦定义了有参构造器,无参就必须显示定义
    public Person(String name){
        //this.name 指的是 String name;
        //name 指的是 传进来的 name
        this.name = name;
    }
    //alt + insert

}


/*


package oop.Demo02;
//一个项目应该只存在一个main方法
public class Application {
public static void main(String[] args) {
//new 实例化一个对象
Person person = new Person();
System.out.println(person.name);//null


}
}

构造器:
1、方法名和类名相同
2、没有返回值
作用:
1、new 本质在调用构造方法
2、初始化对象的值
注意点:Alt + insert
1、定义了有参构造之后,如果想使用无参构造,显示的定义一个无参的构造

*/

3、对象的创建分析

import oop.Demo03.Pet;

/**
 * @title: Application
 * @Author yuenan.ma
 * @Date: 2022/10/30 13:25
 * @Version 1.0
 */

//一个项目应该只存在一个main方法
public class Application {
    public static void main(String[] args) {

        /*
        1、类与对象
            类是一个模板:抽象:对象是一个具体的实例
        2、方法
            定义,调用!
        3、对应的引用
            引用类型:基本类型(8)
                对象是通过引用来操作的:堆--->栈
        4、属性:字段Foeld成员变量
            默认初始化:
                数值0;0.0
                char:u000
                boolean:false
                引用:null

             修饰符 属性类型 属性名 = 属性值!
         5、对象的创建和使用
            - 不需使用 new 关键字创造对象,构造器 Person xiaoma = new Person();
            - 对象的属性  xiaoma.name
            - 对象的用法 xiaoma.sleep()

         6、类
            静态的属性 属性
            动态得行为 方法

        封装、继承、多态
        * */
        //new 实例化一个对象
        Pet dog = new Pet();
        dog.name = "旺财";
        dog.age = 3;
        dog.shout();

        System.out.println(dog.name);
        System.out.println(dog.age);
    }
}

4、面向对象三大特性

封装

该露的露,该藏的藏
我们程序设计要追求**“高内聚,低耦合”**。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。
封装(数据的隐藏)
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。

记住这句话就够了:属性私有,get/set
1、private 不能赋值,名字具有一个私有的权限在学生类里面,无法直接调用;一般情况下我们都会稀有这些属性
![image.png](https://img-blog.csdnimg.cn/img_convert/adcec6db72c50300a163ff60aa60f595.png#averageHue=#fcf8f6&clientId=ue781fb42-76a3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=331&id=u01b01cf9&margin=[object Object]&name=image.png&originHeight=331&originWidth=1412&originalType=binary&ratio=1&rotation=0&showTitle=false&size=42269&status=done&style=none&taskId=uf4ad7a1d-cd0d-410e-89e4-278878edaba&title=&width=1412)
2、public 赋值成功
![image.png](https://img-blog.csdnimg.cn/img_convert/de2c9df2d097c009c2d7f8702bbf1bb2.png#averageHue=#fbf3f1&clientId=ue781fb42-76a3-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=198&id=u37addb28&margin=[object Object]&name=image.png&originHeight=198&originWidth=1401&originalType=binary&ratio=1&rotation=0&showTitle=false&size=30747&status=done&style=none&taskId=u81dbff23-b94f-40ca-8169-6141746eab5&title=&width=1401)

package oop.Demo04;

/**
 * @title: Student
 * @Author yuenan.ma
 * @Date: 2022/11/3 20:43
 * @Version 1.0
 */
//类 private : 私有
public class Student {

//    属性私有
    public String name;//名字
    private int id;//学号
    private char sex;//性别
    private int age;
    //学习()
    //提供一些可以操作这个属性的方法!
    //提供一些 public 的 get、set方法
    //art+insert

    //get 获取这个数据
    public String getName(){
        return this.name;
    }

    //set 给支个数据赋值
    public void setName(String name){
        this.name = name;
    }
    //睡觉()


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age <120 || age>0) {
            this.age = age;
        }else{
            age = 3;
        }
        //this.age = age;
    }
}



import oop.Demo04.Student;

/**
 * @title: Application
 * @Author yuenan.ma
 * @Date: 2022/10/30 13:25
 * @Version 1.0
 */


/*
1、提高程序的安全性,保护数据
2、隐藏代码的细节
3、统一接口
4、系统的可维护性增加了
* */
//一个项目应该只存在一个main方法
public class Application {
    public static void main(String[] args) {
        Student s1 = new Student();
        s1.name = "";
        s1.setName("小马");//s1.可以调用到公共的方法
        System.out.println(s1.getName());



        s1.setAge(70);
        System.out.println(s1.getAge());
    }
}



继承

继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
extands的意思是“扩展”。子类是父类的扩展。
JAVA中类只有单继承,没有多继承!一个儿子只有一个爸爸,但一个爸爸可以有多个儿子
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
继承关系的俩个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
子类和父类之间,从意义上讲应该具有"is a"的关系.

package oop.Demo05;

/**
 * @title: Student
 * @Author yuenan.ma
 * @Date: 2022/11/3 20:43
 * @Version 1.0
 */
//    学生 is 人 : 派生类 子类
    //子类继承了父类,就会调用父类的全部方法
public class Student extends Person{
    //ctrl +H 继承树
}




package oop.Demo05;

/**
 * @title: Person
 * @Author yuenan.ma
 * @Date: 2022/11/5 17:15
 * @Version 1.0
 */

//在JAVA中,所有的类,都默认直接或间接继承Object
//Person 人
public class Person {

    //public
    //protected
    //default
    //private 私有的无法继承的

//    public int money = 10_0000_0000;
    private int money = 10_0000_0000;
    public void say(){
        System.out.println("说了一句话!");
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }
}





import oop.Demo05.Student;

/**
 * @title: Application
 * @Author yuenan.ma
 * @Date: 2022/10/30 13:25
 * @Version 1.0
 */



public class Application {
    public static void main(String[] args) {
        Student student = new Student();
        student.say();
        System.out.println(student.getMoney());

    }
}

object类
super 和this进行对比

import oop.Demo05.Student;

/**
 * @title: Application
 * @Author yuenan.ma
 * @Date: 2022/10/30 13:25
 * @Version 1.0
 */



public class Application {
    public static void main(String[] args) {
        Student student = new Student();
//        student.test("xiaoma");
//        student.test1();


    }
}

package oop.Demo05;

/**
 * @title: Teacher
 * @Author yuenan.ma
 * @Date: 2022/11/5 17:18
 * @Version 1.0
 */
//    老师 is 人
public class Teacher extends Person{
}



package oop.Demo05;

/**
 * @title: Student
 * @Author yuenan.ma
 * @Date: 2022/11/3 20:43
 * @Version 1.0
 */
//    学生 is 人 : 派生类 子类
    //子类继承了父类,就会调用父类的全部方法
public class Student extends Person{
    //ctrl +H 继承树
    protected String name = "小张";


    public Student(){
        //隐藏代码,调用了父类的无参构造
        super();//调用父类的构造器,必须在构造器的第一行
        System.out.println("Student 无参执行了");
    }

    public void print(){
        System.out.println("Student");
    }
    public void test1(){
        print();//Student
        this.print();//Student
        super.print();//Person
    }
    public void test(String name){
        System.out.println(name);//xiaoma
        System.out.println(this.name);//小张
        System.out.println(super.name);//小马
    }
}




package oop.Demo05;

/**
 * @title: Person
 * @Author yuenan.ma
 * @Date: 2022/11/5 17:15
 * @Version 1.0
 */

//在JAVA中,所有的类,都默认直接或间接继承Object
//Person 人
public class Person {
    protected String name = "小马";

    public Person(String name){
        System.out.println("Person 无参执行了");
    }
    public Person(){
        System.out.println("Person 无参执行了");
    }
    //私有的东西无法被继承
    public void print(){
        System.out.println("Person");
    }
}








supper注意点:
    1、supper调用父类的构造方法,必须在构造方方的第一行
    2、supper必须只能出现在子类的方法或者构造器方法中
    3、supper和this不能同时调用构造方法

    VS this:
    代表的对象不同:
        this:本身调用者这个对象
        super:代表父类对象的应用
    前提:
        this:没有继承也可以使用
        super:只能在继承条件才可以使用
    构造方法:
        this();本类的构造
        super();父类的构造
方法重写
package oop.Demo05;

/**
 * @title: A
 * @Author yuenan.ma
 * @Date: 2022/11/7 18:39
 * @Version 1.0
 */
//继承
public class A extends B{
//    public static void test(){
//        System.out.println("A=>test()");
//    }
//    public void test(){
//        System.out.println("A=>test()");
//    }


    //Override 重写
    @Override//注解:有功能的注解!
    public void test() {
//        super.test();
        System.out.println("A=>test()");
    }
}





package oop.Demo05;

/**
 * @title: B
 * @Author yuenan.ma
 * @Date: 2022/11/7 18:39
 * @Version 1.0
 */
//重写都是方法的重写,和属性无关
public class B {
    public void test(){
        System.out.println("B=>test()");
    }
}




import oop.Demo05.A;
import oop.Demo05.B;
import oop.Demo05.Student;

/**
 * @title: Application
 * @Author yuenan.ma
 * @Date: 2022/10/30 13:25
 * @Version 1.0
 */



public class Application {
    public static void main(String[] args) {
      //静态方法和非静态方法区别很大
        // 静态方法://方法的调用只和左边,定义数据的类型有关
        //非静态:重写
        A a = new A();
        a.test();//A

        //父类的引用指向子类
        B b= new A();//子类重写了父类的方法
        b.test();//B
    }
}



重写:需要有继承关系,子类重写父类的方法!
    1、方法名必须相等
    2、参数列表列表必须相同
    3、修饰符:范围可以扩大,public>producted>default>private
    4、抛出的异常:范围可以被缩小,但不能扩大 ClassNotFoundException -->Exception()
重写,子类方法和父类必须一致,方法体不同!

为什么需要重写:
    1、父类的功能,子类不一定需要,或者不一定满足!
    2、alt + insert ; override 重写
多态

动态编译:类型:可扩展性
即同一方法可以根据发送对象的不同而采用多种不同的行为方式。
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)
多态存在的条件
有继承关系
子类重写父类方法
父类引用指向子类对象

package oop.Demo06;

/**
 * @title: Student
 * @Author yuenan.ma
 * @Date: 2022/11/7 19:13
 * @Version 1.0
 */
public class Student extends Person{
    @Override
    public void run() {
        System.out.println("son");
    }

    public void eat(){
        System.out.println("eat");
    }
}


/*
多态注意事项:
    1、多态的方法是多态,属性没有多态
    2、父类和子类,有联系  类型转换异常 ClassCastException
    3、存在条件:继承关系,方法需要重写,父类引用指向子类对象
    father1 f1 = new Son();
    3.1 不能被重写的方法:
        static 方法,属于类,他不属于实例
        final 常量
        private 方法
* */




package oop.Demo06;

/**
 * @title: Person
 * @Author yuenan.ma
 * @Date: 2022/11/7 19:13
 * @Version 1.0
 */
public class Person {
    public void run(){
        System.out.println("run");
    }
}






import oop.Demo06.Person;
import oop.Demo06.Student;

/**
 * @title: Application
 * @Author yuenan.ma
 * @Date: 2022/10/30 13:25
 * @Version 1.0
 */



public class Application {
    public static void main(String[] args) {
        //一个对象的实际类型是确定的
//        new Student();
//        new Person();


        //可以指向的引用类型就不确定了
        //Student 能调用的方法都是自己的或者继承父类的
        Student s1 = new Student();
        //Person 父类型,可以指向子类,但不能调用子类中独有的方法
        Person s2 = new Student();//父类的引用指向子类的类型
        Object s3 = new Student();

//        s2.run();
//        s1.run();//子类重写了父类的方法,执行子类的方法

        //对象能执行哪些方法,主要看对象左边的类型,和右边的关系不大
        //((Student) s2).eat();
        s1.eat();
    }
}


注意:多态是方法的多态,属性没有多态性。
instanceof 类型转换~引用类型之间的转换,判断一个对象是什么类型

package oop.Demo06;

/**
 * @title: Person
 * @Author yuenan.ma
 * @Date: 2022/11/7 19:13
 * @Version 1.0
 */
public class Person {

}



package oop.Demo06;

/**
 * @title: Student
 * @Author yuenan.ma
 * @Date: 2022/11/7 19:13
 * @Version 1.0
 */
public class Student extends Person{
    public void go(){
        System.out.println("go");
    }

}

/*
//Object > String
        //Object > Person > Teather
        //Object > Person > Student
        Object o = new Student();

//        System.out.println(X instanceof Y);//能不能编译通过,接口,是否存在父子关系


        System.out.println(o instanceof Student);//true
        System.out.println(o instanceof Person);//true
        System.out.println(o instanceof Object);//true
        System.out.println(o instanceof Teacher);//false
        System.out.println(o instanceof String);//false
        System.out.println("==============================");
        Person person = new Student();
        System.out.println(person instanceof Student);//true
        System.out.println(person instanceof Person);//true
        System.out.println(person instanceof Object);//true
//        System.out.println(person instanceof Teacher);//false
        //System.out.println(person instanceof String);//false
        System.out.println("==============================");
        Student student = new Student();
        System.out.println(person instanceof Student);//true
        System.out.println(person instanceof Person);//true
        System.out.println(person instanceof Object);//true
//        System.out.println(person instanceof Teacher);//false
//        System.out.println(person instanceof String);//false

* */







import oop.Demo05.Teacher;
import oop.Demo06.Person;
import oop.Demo06.Student;

/**
 * @title: Application
 * @Author yuenan.ma
 * @Date: 2022/10/30 13:25
 * @Version 1.0
 */



public class Application {
    public static void main(String[] args) {
        //类型之间的转换:基本类型转换,高低 64 32 16

       /* //高                       低
        Person student = new Student();
        //student将这个对象转换为Student类型,我们就可以使用Student类型的方法
        Student student1 = (Student) student;
        student1.go();*/

        //子类转换为父类,可能丢失自己本来的一些方法
        Student student = new Student();
        student.go();
        Person person = student;
    }
}


/*
多态
1、父类的引用指向子类的对象
2、把子类转换为父类,向上转型;
3、把父类转换成子类,向下转型,强制转换
4、方便方法的调用,减少重复的代码,提升代码的利用率

抽象:封装、继承、多态   抽象类,接口
* */




package oop.Demo07;

/**
 * @title: Person
 * @Author yuenan.ma
 * @Date: 2022/11/8 18:51
 * @Version 1.0
 */
public class Person {
    {
        //匿名代码块2,赋初始值
        System.out.println("匿名代码块");
    }
    static{
        //静态代码块1,只执行一次
        System.out.println("静态代码块");
    }

    public  Person(){//3
        System.out.println("构造方法");
    }

    public static void main(String[] args) {
        Person person = new Person();
        System.out.println("=======================");
        Person person1 = new Person();


    }
}





package oop.Demo07;

/**
 * @title: Student
 * @Author yuenan.ma
 * @Date: 2022/11/7 20:27
 * @Version 1.0
 */
//static :在类中修饰成员变量;方法里面,修饰静态成员方法;加在属性上,静态属性
public class Student {
    private static int age;//静态的变量 多线程
    private double score;//非静态的变量

    public void run(){

    }
    public static void go(){

    }


    public static void main(String[] args) {
//        Student student = new Student();
        System.out.println(Student.age);//静态变量
//        System.out.println(Student.score);//报错,非静态字段
        System.out.println(student.age);
        System.out.println(student.score);

        new  Student().run();
//        run()不能调用普通方法
    }
}





package oop.Demo07;

import static java.lang.Math.random;
import static java.lang.Math.PI;
/**
 * @title: Test
 * @Author yuenan.ma
 * @Date: 2022/11/8 18:58
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        System.out.println(random());
        System.out.println(PI);
    }
}


5、抽象类和接口

抽象类

abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。

package oop.Demo08;

/**
 * @title: A
 * @Author yuenan.ma
 * @Date: 2022/11/8 19:17
 * @Version 1.0
 */
//抽象类的所有方法,继承了它的子类,都必须要实现它的方法,除非子类也是 abstract 时,
public class A extends Action{
    @Override
    public void doSomething() {

    }
}




package oop.Demo08;

/**
 * @title: Action
 * @Author yuenan.ma
 * @Date: 2022/11/8 19:14
 * @Version 1.0
 */
//abstract 抽象类,类 extends:单继承,无多继承(接口可以解决)
public abstract class Action {

    //约束~有人帮我们实现~
    //abstract 抽象方法,只有方法名字,没有方法的实现
    public abstract void doSomething();

    //抽象类特点,
    //  1、不能new这个抽象类,只能靠它的子类去实现它,约束
    //  2、抽象类里面可以写普通方法
    //  3、抽象方法必须在抽象类中
    //抽象的抽象:约束

    //思考题? new ,存在构造器是什么?存在的意义(抽象出来,提高开发效率)
}


接口

普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有!
接口:只有规范!自己无法写方法专业的约束!约束和实现分离:面向接口编程
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能.….”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你好人,则必须干掉坏人;如果你是坏人,则必须欺负好人。
接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
OO的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
声明类的关键字是class,声明接口的关键字是interface

作用:
    1、约束
    2、定义一些方法,让不同的人实现
    3、方法都是 public abstract
    4、常量都是 public static final
    5、接口不能被实例化,接口没有构造方法
    6implements可以实现多个接口
    7、必须要重写接口中的方法

package oop.Demo09;
//抽象的思维 架构师
//interface 定义的关键字,接口都需要有实现类
//类可以实现接口 implements 接口
//实现了接口的类,就需要重写接口中的方法
public interface UserService {

    //常量 public static final
    int age =  99;
    //接口中的所有定义其实都是抽象的public abstract
    void add(String name);
    void delete(String name);
    void update(String name);
    void query(String name);
}




package oop.Demo09;

/**
 * @title: TimeService
 * @Author yuenan.ma
 * @Date: 2022/11/8 19:40
 * @Version 1.0
 */
public interface TimeService {
    void timer();
}




package oop.Demo09;

/**
 * @title: UserServiceImpl
 * @Author yuenan.ma
 * @Date: 2022/11/8 19:37
 * @Version 1.0
 */
//interface 定义的关键字,接口都需要有实现类
//类可以实现接口 implements 接口
//实现了接口的类,就需要重写接口中的方法

//利用接口从侧面实现多继承
public class UserServiceImpl implements UserService,TimeService{

    @Override
    public void add(String name) {

    }

    @Override
    public void delete(String name) {

    }

    @Override
    public void update(String name) {

    }

    @Override
    public void query(String name) {

    }

    @Override
    public void timer() {

    }
}

6、内部类及OOP实战

内部类(必修)
内部类就是在一个类的内部在定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
1.成员内部类

package oop.Demo10;

/**
 * @title: Outer
 * @Author yuenan.ma
 * @Date: 2022/11/8 19:55
 * @Version 1.0
 */
public class Outer {
    private int id=10;
    public void out(){
        System.out.println("这是外部类的方法");
    }

    public class Inner{
        public void in(){
            System.out.println("这是内部类的方法");
        }

        //
        public void getId(){
            System.out.println(id);
        }
    }


}


//一个java类中可以有多个class类,但只能有一个public class
class A{
    public static void main(String[] args){

    }
}





import oop.Demo05.Teacher;
import oop.Demo06.Person;
import oop.Demo06.Student;
import oop.Demo10.Outer;

/**
 * @title: Application
 * @Author yuenan.ma
 * @Date: 2022/10/30 13:25
 * @Version 1.0
 */



public class Application {
    public static void main(String[] args) {
        Outer outer = new Outer();

        //通过这个外部类来实例化内部类
        Outer.Inner inner = outer.new Inner();
        inner.in();
        inner.getId();
    }
}

2.静态内部类

import oop.Demo05.Teacher;
import oop.Demo06.Person;
import oop.Demo06.Student;
import oop.Demo10.Outer;

/**
 * @title: Application
 * @Author yuenan.ma
 * @Date: 2022/10/30 13:25
 * @Version 1.0
 */



public class Application {
    public static void main(String[] args) {
        Outer outer = new Outer();

        //通过这个外部类来实例化内部类
        Outer.Inner inner = outer.new Inner();
        inner.in();
        inner.getId();
    }
}




package oop.Demo10;

/**
 * @title: Outer
 * @Author yuenan.ma
 * @Date: 2022/11/8 19:55
 * @Version 1.0
 */
public class Outer {
    private static int id=10;
    public void out(){
        System.out.println("这是外部类的方法");
    }

    public class Inner{
        public void in(){
            System.out.println("这是内部类的方法");
        }

        //
        public void getId(){
            System.out.println(id);
        }
    }


}

3.局部内部类

package oop.Demo10;

/**
 * @title: Outer
 * @Author yuenan.ma
 * @Date: 2022/11/8 19:55
 * @Version 1.0
 */
public class Outer {

}

//一个java类中可以有多个class类,但只能有一个public class
class A{
    public static void main(String[] args) {

    }
}

4.匿名内部类

package oop.Demo10;

/**
 * @title: Test
 * @Author yuenan.ma
 * @Date: 2022/11/8 20:27
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        Apple apple = new Apple();//实例化对象,并把名字赋值给 apple

        new Apple().eat();  //匿名 没有名字初始化类,不用将实例保存到变量中
        UserService userService = new UserService() {

            @Override
            public void hello() {

            }
        };//实现接口的匿名调用
    }
}
class Apple{
    public void eat(){
        System.out.println("1");
    }
}

interface UserService{
    void hello();
}

异常机制Exception

1、什么是异常

实际工作中,遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求、你的程序要打开某个文件,这个文件可能不存在或者文件格式不对,你要读取数据库的数据,数据可能是空的等。我们的程序再跑着,内存或硬盘可能满了。等等。
软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英文是:Exception,意思是例外。这些,例外情况,或者叫异常,怎么让我们写的程序做出合理的处理。而不至于程序崩溃。
异常指程序运行中出现的不期而至的各种状况,如:文件找不到
、网络连接失败、非法参数等。
异常发生在程序运行期间,它影响了正常的程序执行流程。

简单分类

要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
异常处理框架
Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。在Java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。
![image.png](https://img-blog.csdnimg.cn/img_convert/85d3a319d47571290b57f937e9a09021.png#averageHue=#e1e1e1&clientId=ud578616c-87f2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=928&id=u1293ae14&margin=[object Object]&name=image.png&originHeight=928&originWidth=1405&originalType=binary&ratio=1&rotation=0&showTitle=false&size=286085&status=done&style=none&taskId=ucf8ae375-0c01-4926-93d2-4d3abad0cca&title=&width=1405)
检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
错误ERROR:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。

2、异常体系结构

Error

Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
Java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止;
还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。

Exception

在Exception分支中有一个重要的子类RuntimeException(运行时异常)
ArraylndexOutOfBoundsException(数组下标越界)
NullPointerException(空指针异常)
ArithmeticException(算术异常)
MissingResourceException(丢失资源)
ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。
这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;
Error和Exception的区别:Error通常是灾难性的致命的错误是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;
Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。

3、Java异常处理机制

抛出异常
捕获异常

异常处理五个关键字
try:尝试处理的地方
catch:捕获
finally:最终返回值,不管执不执行都会走到
throw,throws:抛出异常

package oop.Demo11;

/**
 * @title: Test
 * @Author yuenan.ma
 * @Date: 2022/11/8 20:53
 * @Version 1.0
 */
public class Test {
    public static void main(String[] args) {
        int a = 1;
        int b = 0;
        try{//try监控区域
            System.out.println(a/b);
        }catch (ArithmeticException e){//捕获异常
            System.out.println("程序处出现异常,变量b不能为0");
        }finally {//处理善后工作
            System.out.println("finally");
        }
  //finally一般可以不用 ,假设IO,资源,关闭
    }
}

4、处理异常

5、自定义异常

6、总结

流Stream、文件File和IO

读取控制台输入

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
1、System.in完成控制台输入
2、为了获得一个绑定到控制台的字节流,可以把System.in包装在BufferedReader对象中来创建一个字节流
3、BufferedReader 对象创建后,可以使用read() 方法从控制台读取一个字符串

从控制台读取多字符输入

从BufferedReader 对象读取一个字符要使用read()方法,
int read() throws IOException
调用read()方法时,会从输入流读取一个字符并把该字符作为整数值返回,当流结束的时候返回-1.该方法抛出IOException

//用read()方法从控制台不断读取字符直到用户输入q
//使用BufferedReader 在控制台读取字符

import java.io.*;
public class BRead{
    public static void main(String[] args) throws IOException {
        char c;
        //使用System.in 创建 BufferedReader
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("输入字符,按下'q'键退出");
        //读取字符
        do{
            c = (char) br.read();
            System.outprintln(c);
        }while(c != 'q');
    }
}

从控制台读取字符串

从标准输入读取一个字符串需要使用BufferedReader 的 readLine()方法
String readLine() throws IOException

//使用 BufferedReader 在控制台读取字符
import java.io.*;

public class BReadLines{
    public static void main(String[] args)throws IOException{
        //使用 System.in 创建 BufferedReader
        BufferedReader br = new BUfferedReader (new InputStreamReader(System.in));
        String str;
        System.out.println("Enter lines of text.");
        System.out.println("Enter 'end' to quit.");
        do{
            str = br.readLine();
            System.out.println(str);
        }while (! str.equals("end"));
    }
}
Java Scanner 类

java.util.Scanner 是Java的新特性,通过Scanner 类获取用户的输入
Scanner s = new Scanner(System.in);
通过Scanner类的next()与nextLine()方法获取输入的字符串,在读取前,使用hasNext 与hasNextLine 判断是否还有输入的数据

//使用next方法
import java.util.Scanner;

public class ScannerDemo{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        //从键盘接收数据
        //next 方式接受字符串
        System.out.println("next方式接受: ");
        //判断是否还有输入
        if(scan.hasNext()){
            String str1 = scan.next();
            System.out.println("输入的数据为: " + str1);
        }
        scan.close();
    }
}

使用nextLine方法:

import java.util.Scanner;

public class ScannerDEMO{
    PUBLIC static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        //从键盘接收数据

        //nextLine方式接受字符串
        System.out.println("nextLine方式接收:");
        //判断是否还有输入
        if(scan.hasNextLine()){
            String str2= scan.nextLine();
            System.out.println("输入的数据为: " + str);
        }
        scan.close();
    }
}

多线程详解(Java.Thread)

线程简介

任务,进程,线程,多线程
普通方法调用和多线程

![image.png](https://img-blog.csdnimg.cn/img_convert/b7a40fcd985fdbedce45746c4a3e1695.png#averageHue=#fcfcfa&clientId=u99d6604c-bd4a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=377&id=u0681502d&margin=[object Object]&name=image.png&originHeight=377&originWidth=644&originalType=binary&ratio=1&rotation=0&showTitle=false&size=113454&status=done&style=none&taskId=ubb4b89cb-7739-49d3-ac9c-771c620fbc2&title=&width=644)

程序.进程.线程
Process与Thread

说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
而进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位
通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的的单位。
注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。

本章核心概念:

线程就是独立的执行路径;
在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
main()称之为主线程,为系统的入口,用于执行整个程序;
在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;线程会带来额外的开销,如cpu调度时间,并发控制开销。
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

线程创建(Thread、Runnable、Callable)
三种创建方式

Thread class 继承Thread类(重点)
Runnable接口 实现Runnable接口(重点)
Callable接口 实现Callable接口(了解)

Thread 学习提示:查看JDK帮助文档

自定义线程类继承Thread类
重写run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程

Thread class 继承Thread类(重点)

![image.png](https://img-blog.csdnimg.cn/img_convert/77406a9f3075dd4cefe5df246372305d.png#averageHue=#faf9f9&clientId=u99d6604c-bd4a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=338&id=u4d63a42e&margin=[object Object]&name=image.png&originHeight=338&originWidth=601&originalType=binary&ratio=1&rotation=0&showTitle=false&size=96782&status=done&style=none&taskId=u23bff796-9cf4-4c83-933d-68df9c43d3f&title=&width=601)

package mynthread;

/**
 * @title: TestThread1
 * @Author yuenan.ma
 * @Date: 2022/11/20 13:58
 * @Version 1.0
 */
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
//    总结:注意,线程开启不一定立即执行,由CPU调度执行
public class TestThread1 extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码---"+i);
        }
    }

    public static void main(String[] args) {
//        main线程,主线程
        //创建一个线程对象
        TestThread1 testThread1 = new TestThread1();
        //调用start线程对象
        testThread1.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习多线程---"+i);
        }
    }
}

//案例:下载图片
![image.png](https://img-blog.csdnimg.cn/img_convert/b834b516f046661790e8b7cceb371ad3.png#averageHue=#fcfcfc&clientId=u99d6604c-bd4a-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=358&id=u5e32ece7&margin=[object Object]&name=image.png&originHeight=358&originWidth=806&originalType=binary&ratio=1&rotation=0&showTitle=false&size=142454&status=done&style=none&taskId=ub074e94e-4bcb-4b70-a537-4b6c2c7f55f&title=&width=806)
![image.png](https://img-blog.csdnimg.cn/img_convert/01960b4dfc306f5ec27cdde914bc6d51.png#averageHue=#f3eae9&clientId=uc413afa7-1551-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=230&id=fBKCb&margin=[object Object]&name=image.png&originHeight=230&originWidth=611&originalType=binary&ratio=1&rotation=0&showTitle=false&size=26241&status=done&style=none&taskId=uf6ac707c-f1ee-4e5f-8b83-7781d8d3a55&title=&width=611)
![image.png](https://img-blog.csdnimg.cn/img_convert/66d17bd336738265e10228ab734f9c4f.png#averageHue=#f7f6f4&clientId=uda74408e-64e8-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=672&id=u1c3bdb2c&margin=[object Object]&name=image.png&originHeight=672&originWidth=650&originalType=binary&ratio=1&rotation=0&showTitle=false&size=81585&status=done&style=none&taskId=u3233586e-ab22-41fe-b985-faf75901a77&title=&width=650)

package mynthread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * @title: TestThread2,同时下载三张图片
 * @Author yuenan.ma
 * @Date: 2022/11/20 14:52
 * @Version 1.0
 */
//练习Thread,实现一个多线程同步下载图片
public class TestThread2 extends Thread{

    private String url;//网络图片地址
    private String name;//保存的文件名

    //手写构造器 public TT2
    public TestThread2(String url,String name){
        this.url = url;
        this.name = name;
    }
    //线程执行体,重写run方法,下载图片线程的执行体
    @Override
    public void run() {
        WebDownLoader webDownLoader = new WebDownLoader();
        webDownLoader.downlodaer(url,name);
        System.out.println("下载文件名为:"+name);

    }

    public static void main(String[] args) {
        TestThread2 testThread1 = new TestThread2("https://img-blog.csdnimg.cn/d96a157497ba41a8a1c8f53dcc46fa35.png","1.jpg");
        TestThread2 testThread2 = new TestThread2("https://img-blog.csdnimg.cn/d96a157497ba41a8a1c8f53dcc46fa35.png","2.jpg");
        TestThread2 testThread3 = new TestThread2("https://img-blog.csdnimg.cn/d96a157497ba41a8a1c8f53dcc46fa35.png","3.jpg");


        testThread1.start();
        testThread2.start();
        testThread3.start();
    }
}

//下载器
class WebDownLoader{
    //下载方法
    public void downlodaer(String url,String name){
        try {
//            import org.apache.commons.io.FileUtils;包下的
            //把网上的网页地址变成一个文件,拷贝网络上url到一个地址
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}
Runnable接口 实现Runnable接口(重点)

定义MyRunnable类实现Runnable接口
实现run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
![image.png](https://img-blog.csdnimg.cn/img_convert/3b8da930201f64fe7e6852cd29487842.png#averageHue=#f6f5f4&clientId=u3a37ff12-feb5-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=829&id=u0da70f4f&margin=[object Object]&name=image.png&originHeight=829&originWidth=1632&originalType=binary&ratio=1&rotation=0&showTitle=false&size=375378&status=done&style=none&taskId=u4eb9228a-463e-41c7-aa0c-23deb077840&title=&width=1632)

小结:

继承Thread类
子类继承Thread类具备多线程能力
启动线程:子类对象. start()
不建议使用:避免OOP单继承局限性

线程实现(重点)

实现Runnable接口

实现接口Runnable具有多线程能力
启动线程:传入目标对象+Thread对象.start()
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

package mynthread;

/**
 * @title: TestThread4,多个线程同时操作一个对象,买火车票例子
 * 发现问题:多个线程操作同一个自愿的情况下,线程不安全,数据紊乱。(线程并发问题)
 * @Author yuenan.ma
 * @Date: 2022/11/22 19:26
 * @Version 1.0
 */
public class TestThread4 implements Runnable{


    //票数
    private int ticketNums = 10;
    @Override
    public void run(){
        while(true){
            if(ticketNums <= 0){
                break;
            }
            //模拟延时
            try {
                Thread.sleep(100);//纳秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums --+"票");//出现多个线程同时操作一个资源
        }
    }

    public static void main(String[] args) {
        TestThread4 testThread4 = new TestThread4();

        new Thread(testThread4,"小马").start();
        new Thread(testThread4,"老师").start();
        new Thread(testThread4,"黄牛党").start();
    }
}

案例:龟兔赛跑-Race
1.首先来个赛道距离,然后要离终点越来越近
2.判断比赛是否结束
3.打印出胜利者
4.龟兔赛跑开始
5.故事中是乌龟赢的,兔子需要睡觉,所以我们来模拟兔子睡觉
6.终于,乌龟赢得比赛

package mynthread;

/**
 * @title: Race
 * @Author yuenan.ma
 * @Date: 2022/11/22 19:39
 * @Version 1.0
 */
public class Race implements Runnable{
    //胜利者,静态常量
    private String winner;
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            //模拟兔子睡觉
            if (Thread.currentThread().getName().equals("兔子") && i%10 == 0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //判断比赛是否结束
            boolean falg = gameOver(i);
            if (falg){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");

        }
    }

    //判断是否完成比赛
    private boolean gameOver(int steps){
        //判断是否有胜利者
        if (winner != null){//已经存在胜利者了
            return true;

        }{
            if (steps >= 100){
                winner = Thread.currentThread().getName();
                System.out.println("winner is"+ winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race();

        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

实现Callable接口(了解即可)

1.实现Callable接口,需要返回值类型

  1. 重写call方法,需要抛出异常

3.创建目标对象
4.创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);

  1. 提交执行:Future result1 = ser.submit(t1);

6.获取结果: boolean r1 = result1.get()
7.关闭服务:ser.shutdownNow();
演示:利用callable改造下载图片案例

package mynthread.ThreadDemo01;

import mynthread.TestThread2;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

/**
 * @title: TestCallable,线程创建方式三:实现callable接口
 * callable的好处
 * 1、可以定义返回值
 * 2、可以抛出异常
 * @Author yuenan.ma
 * @Date: 2022/11/23 22:34
 * @Version 1.0
 */
public class TestCallable implements Callable<Boolean> {

    private String url;//网络图片地址
    private String name;//保存的文件名

    //手写构造器 public TT2
    public TestCallable(String url,String name){
        this.url = url;
        this.name = name;
    }
    //线程执行体,重写run方法,下载图片线程的执行体
    @Override
    public Boolean call() {
        WebDownLoader webDownLoader = new WebDownLoader();
        webDownLoader.downlodaer(url,name);
        System.out.println("下载文件名为:"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable testThread1 = new TestCallable("https://img-blog.csdnimg.cn/d96a157497ba41a8a1c8f53dcc46fa35.png","1.jpg");
        TestCallable testThread2 = new TestCallable("https://img-blog.csdnimg.cn/d96a157497ba41a8a1c8f53dcc46fa35.png","2.jpg");
        TestCallable testThread3 = new TestCallable("https://img-blog.csdnimg.cn/d96a157497ba41a8a1c8f53dcc46fa35.png","3.jpg");

        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1 = ser.submit(testThread1);
        Future<Boolean> r2 = ser.submit(testThread2);
        Future<Boolean> r3 = ser.submit(testThread3);


            boolean rs1= r1.get();
            boolean rs2= r2.get();
            boolean rs3= r3.get();
        System.out.println(rs1);
        System.out.println(rs2);
        System.out.println(rs3);
        ser.shutdownNow();

    }


}
//下载器
class WebDownLoader{
    //下载方法
    public void downlodaer(String url,String name){
        try {
//            import org.apache.commons.io.FileUtils;包下的
            //把网上的网页地址变成一个文件,拷贝网络上url到一个地址
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

静态代理
![image.png](https://img-blog.csdnimg.cn/img_convert/c30dcd69cbce0c77a0420bc7e29aa59d.png#averageHue=#b4a99e&clientId=udc517390-35ef-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=259&id=MXwgj&margin=[object Object]&name=image.png&originHeight=259&originWidth=578&originalType=binary&ratio=1&rotation=0&showTitle=false&size=111395&status=done&style=none&taskId=u947b7af8-78fd-406d-a98b-2427f7e0715&title=&width=578)

package lambda.proxystatic;

/**
 * @title: StaticProxy
 * 静态代理模式总结:
 * 真是对象和代理对象都要实现同一个接口
 * 代理对象要代理真实角色
 *
 * 好处:
 *  代理对象可以做很多真实对象做不了的事情
 *  真实对象专注做自己的事情
 * @Author yuenan.ma
 * @Date: 2022/11/26 9:53
 * @Version 1.0
 */
public class StaticProxy {
    public static void main(String[] args) {


//        You you = new You();
        new Thread(()-> System.out.println("我爱你")).start();
        new WeddingCompany(new You()).HappyMarry();

//        WeddingCompany weddingCompany = new WeddingCompany(you);
//        weddingCompany.HappyMarry();
    }
}
interface Marry{
    //人生四大喜事
    //久旱逢甘露
    //他乡遇故知
    //洞房花烛夜
    //金榜题名时
    void HappyMarry();
}
//真实角色,参加结婚
class You implements Marry{

     @Override
    public void HappyMarry() {
        System.out.println("我要结婚啦,超开心");
    }
}
//代理角色,帮助我结婚
class WeddingCompany implements Marry{
    private Marry target;

    //创建构造方法
    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        //代理谁---》真是目标角色
        before();
        this.target.HappyMarry();//这就是真实对象
        after();
    }

    private void after() {
        System.out.println("结婚之后收尾款");
    }

    private void before() {
        System.out.println("结婚之前布置现场");
    }
}

Lamda表达式
![image.png](https://img-blog.csdnimg.cn/img_convert/329412490cac98be28a435667247cefc.png#averageHue=#eeeeed&clientId=udc517390-35ef-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=418&id=yEbqx&margin=[object Object]&name=image.png&originHeight=418&originWidth=719&originalType=binary&ratio=1&rotation=0&showTitle=false&size=122203&status=done&style=none&taskId=u5f8cb33a-124d-4416-9367-c247bb50ce0&title=&width=719)
λ希腊字母表中排序第十一位的字母,英语名称为Lambda
避免匿名内部类定义过多
其实质属于函数式编程的概念
理解Functional lnterface(函数式接口)是学习Java8 lambda表达式的关键所在。
函数式接口的定义:
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
public interface Runnable {
public abstract void run();
}
对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。

package lambda.proxystatic;

/**
 * @title: TestLambda2
 * @Author yuenan.ma
 * @Date: 2022/11/26 16:51
 * @Version 1.0
 */
public class TestLambda2 {

    //静态代理
    static class Love implements ILove{

        @Override
        public void love(int a,int b,int c) {
            System.out.println(" I love you==>" +a);
        }
    }
    public static void main(String[] args) {

        ILove love = null;
        class Love implements ILove{

            @Override
            public void love(int a,int b,int c) {
                System.out.println(" I love you==>" +a);
            }
        }

//        匿名类
//        ILove love = new ILove() {
//            @Override
//            public void love(int a,int b,int c) {
//                System.out.println(" I love you==>" +a);
//                System.out.println(" I love you==>" +b);
//            }
//        };

//        Lambda
         love = (int a,int b,int c) ->{
            System.out.println(" I love you ==>" +a);
            System.out.println(" I love you too==>" +b);
        };
//简化1:去掉参数类型
        love = (a,b,c)->{
            System.out.println("I love you ==>"+a);
            System.out.println("I love you too==>"+b);
        };
//简化2:简化括号
//        love = a->{
//            System.out.println("I love you ==>"+a);
//        };
//简化3:简化花括号
//        love = a -> System.out.println("I love you ==>"+a);

        /*
        * 总结:
        * lambda 表达式只能有一行代码的情况下才能简化成为一行,如果有多行,就只能用代码块包裹
        * 前提接口必须是函数式接口
        * 多个参数也可以去掉参数类型,要去掉就都去掉,必须加括号
        * */



//        ILove love = new Love();
        love.love(521,520,555);


    }
}
interface ILove{
    void love(int a,int b,int c);
}
class Love implements ILove{

    @Override
    public void love(int a,int b,int c) {
        System.out.println(" I love you==>" +a+b);
    }
}

线程状态,五大状态

![image.png](https://img-blog.csdnimg.cn/img_convert/ef9402f4b2f3cf9e5bf6210b23617ca9.png#averageHue=#f1ebea&clientId=uceea16bc-20e5-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=510&id=u267691de&margin=[object Object]&name=image.png&originHeight=510&originWidth=1441&originalType=binary&ratio=1&rotation=0&showTitle=false&size=235455&status=done&style=none&taskId=u0b0b8e78-7270-49dc-bc02-b1cc6df80d0&title=&width=1441)
new()-start()-CPU运行调度-sleep(),swat()-死亡
![image.png](https://img-blog.csdnimg.cn/img_convert/a0dd98e05a564d5f44c431e004518461.png#averageHue=#ececec&clientId=uceea16bc-20e5-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=932&id=ubef0abad&margin=[object Object]&name=image.png&originHeight=932&originWidth=1795&originalType=binary&ratio=1&rotation=0&showTitle=false&size=402156&status=done&style=none&taskId=ucc1b1d3d-c070-44b3-95fe-d5846223b5f&title=&width=1795)

![image.png](https://img-blog.csdnimg.cn/img_convert/8ba3738850df3ba7312c75890a98a2b0.png#averageHue=#abbedc&clientId=uceea16bc-20e5-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=715&id=u12d63bb7&margin=[object Object]&name=image.png&originHeight=715&originWidth=1467&originalType=binary&ratio=1&rotation=0&showTitle=false&size=407149&status=done&style=none&taskId=u5c1ad4fc-6e0b-4914-a9c5-c0b92e32f04&title=&width=1467)
setPriority(int newPriority):更改线程的优先级(普瑞尔忒)
static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休
void join():等待该线程终止
static void yield():暂停当前正在执行的线程对象,并执行其他线程
void interrupt():中断线程,别用这个方式
boolean isAlive():测试线程是否处于活动状态

停止线程
不推荐使用JDK提供的stop()、destroy()方法。【已废弃】
推荐线程自己停止下来
建议使用一个标志位进行终止变量当flag=false,则终止线程运行。

public class TestStop implements Runnable {
    //1、线程中定义线程体使用的标识
    private boolean flag =true;

    @Override
    public void run(){
        //2、线程体使用该标识
        while (flag){
            System.out.println("run... Thread");
        }
    }
    //3、对外提供方法改变标识
    public void stop(){
        this.flag = false;
    }
}
package mynthread.ThreadDemo01;

/**
 * @title: TestStop
 * 测试停止线程
 * 1、建议线程正常停止--->利用次数,不建议使用死循环
 * 2、建议使用标志位--->设置一个标志位
 * 3、不要使用 stop 或者 destory 等过时或者JDK不建议使用的方法
 * @Author yuenan.ma
 * @Date: 2022/11/28 20:17
 * @Version 1.0
 */
public class TestStop implements Runnable{


    //1、设置一个标识位
    private boolean flag = false;

    @Override
    public void run() {
        int i=0;
        while (flag){
            System.out.println("run .... Thread"+i++);
        }
    }

    //2、设置一个公开的方法停止线程,转换标志位
    public void stop(){
        this.flag = false;
    }
    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main + "+i);
            if(i==900){
                //调用stop方法切换标志位,让线程停止
                testStop.stop();
                System.out.println("线程该停止了");
            }
        }

    }
}

线程休眠
sleep(时间)指定当前线程阻塞的毫秒数;
sleep存在异常InterruptedException;
sleep时间达到后线程进入就绪状态;
sleep可以模拟网络延时,倒计时等。
每一个对象都有一个锁,sleep不会释放锁;

package mynthread.ThreadDemo01;

import mynthread.TestThread4;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.SimpleFormatter;

/**
 * @title: TestSleep模拟倒计时,,,
 * 模拟网络延时,倒计时
 * @Author yuenan.ma
 * @Date: 2022/11/29 20:14
 * @Version 1.0
 */
public class TestSleep2 {
    public static void main(String[] args) {
//        try {
//            tenDown();
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
        //打印当前系统时间
        Date startTime =  new Date(System.currentTimeMillis());//获取系统当前时间
        while (true){
            try {
                Thread.sleep(1000);//休息一秒
                //打印时间:SimpleDateFormat 时间格式化工厂,
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));

                startTime =  new Date(System.currentTimeMillis());//更新当前时间
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        }
    }
    //模拟倒计时
    public static void tenDown() throws InterruptedException {
        int num = 10;
        while(true){
            Thread.sleep(1000);
            System.out.println(num--);
            if (num <0){
                break;
            }
        }
    }

}

线程礼让yield
礼让线程,让当前正在执行的线程暂停,但不阻塞
将线程从运行状态转为就绪状态
让cpu重新调度,礼让不一定成功!看CPU心情

package mynthread.ThreadDemo01;

/**
 * @title: TestYield测试礼让线程
 * 礼让不一定成功,看CPU心情
 * @Author yuenan.ma
 * @Date: 2022/11/29 22:04
 * @Version 1.0
 */
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }
}
class MyYield implements Runnable{


    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

Join
Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
可以想象成插队

package mynthread.ThreadDemo01;

/**
 * @title: TestJoin测试join方法想象成插队
 * @Author yuenan.ma
 * @Date: 2022/11/29 22:13
 * @Version 1.0
 */
public class TestJoin implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("线程vip来了"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //启动我们的线程
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        //主线程
        for (int i = 0; i < 500; i++) {
            if(i==200){
                thread.join();//插队
            }
            System.out.println("main"+i);
        }
    }
}

观测状态观测
Thread.State
线程状态。线程可以处于以下状态之一:
NEW
尚未启动的线程处于此状态。
RUNNABLE
在Java虚拟机中执行的线程处于此状态。
BLOCKED
被阻塞等待监视器锁定的线程处于此状态。
WAITING
正在等待另一个线程执行特定动作的线程处于此状态。
TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。TERMINATED
已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态。

package mynthread.ThreadDemo01;

/**
 * @title: TestState,
 * @Author yuenan.ma
 * @Date: 2022/11/30 18:59
 * @Version 1.0
 */
public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("/");
            }
        });

        //观察状态
        Thread.State state = thread.getState();
        System.out.println(state);

        //观察启动后
        thread.start();//启动线程
        state = thread.getState();
        System.out.println(state);//Run

        while (state != Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
            Thread.sleep(100);
            state = thread.getState();//更新线程状态
            System.out.println(state);//输出状态
        }

    }
}

线程优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
线程的优先级用数字表示,范围从1~10.
Thread.MIN_PRIORITY= 1;
Thread.MAX_PRIORITY= 10;
Thread.NORM_PRIORITY = 5;
使用以下方式改变或获取优先级
getPriority() . setPriority(int xxx)
优先级的设定建议在start()调度前
优先级低只是意味着获取调度的概率低,并不是优先级低就不会被调用,这都是看CPU的调度

package mynthread.ThreadDemo01;

import mynthread.TestThread4;

/**
 * @title: TestPriority测试线程的优先级
 * @Author yuenan.ma
 * @Date: 2022/11/30 19:25
 * @Version 1.0
 */
public class TestPriority {
    public static void main(String[] args) {
        //主线程默认优先级
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();
        Thread thread1 = new Thread(myPriority);
        Thread thread2 = new Thread(myPriority);
        Thread thread3 = new Thread(myPriority);
        Thread thread4 = new Thread(myPriority);
        Thread thread5 = new Thread(myPriority);
        Thread thread6 = new Thread(myPriority);


        //先设置优先等级,在启动
        thread1.start();

        thread2.setPriority(1);
        thread2.start();

        thread3.setPriority(4);
        thread3.start();

        thread4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY = 10
        thread4.start();

        thread5.setPriority(8);
        thread5.start();

//        thread5.setPriority(-1);
//        thread5.start();
//
//        thread6.setPriority(11);
//        thread6.start();

    }
}
class MyPriority implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}

守护(daemon)线程
线程分为**用户线程(main(),gc())**和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
如,后台记录操作日志,监控内存,垃圾回收等待…人生不过三万天

package mynthread.ThreadDemo01;

/**
 * @title: TestDaemon测试守护线程
 * 上帝守护你
 * @Author yuenan.ma
 * @Date: 2022/11/30 22:27
 * @Version 1.0
 */
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();


        Thread thread = new Thread(god);
        thread.setDaemon(true);//默认是false表示是用户线程,正常的线程都是用户线程

        thread.start();
        new Thread(you).start();//你 用户线程启动...
//        new Thread(god).start();//你 用户线程启动...
    }
}


//上帝
class God implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("上帝保佑着你");
        }
    }
}

//你
class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("你一生都开心的活着");
        }
        System.out.println("-========goodbye!=========");
    }
}

线程同步(重点)

多个线程操作同一个资源
并发
并发∶同一个对象被多个线程同时操作
线程同步
现实生活中,我们会遇到”同一个资源,多个人都想使用”的问题,比如,食堂排队打饭,每个人都想吃饭,最天然的解决办法就是﹐排队.一个个来.
处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象.这时候我们就需要线程同步.线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用
队列和锁
由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性﹐在访问时加入锁机制synchronized, 当一个线程获得对象的排它锁,独占资源﹐其他线程必须等待,使用后释放锁即可.存在以下问题:
一个线程持有锁会导致其他所有需要此锁的线程挂起;
在多线程竞争下,加锁﹐释放锁会导致比较多的上下文切换和调度延时,引起性能问题﹔
如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题.

package mynthread.syn;

/**
* @title: UnsafeBank
* 不安全的取钱
* 两个人去银行取钱
* 账户
* @Author yuenan.ma
* @Date: 2022/12/4 14:20
* @Version 1.0
*/
public class UnsafeBank {
    public static void main(String[] args) {
        //账户
        Account account = new Account(100,"结婚基金");
        Drawing you = new Drawing(account,50,"你");
        Drawing girlFriend = new Drawing(account,100,"girlFriend");

        you.start();
        girlFriend.start();
    }
}
//银行:模拟取款
class Drawing extends Thread{

    Account account;//账户
    //取了多少钱
    int drawingMoney;
    //手里有多少钱
    int nowMoney;

    public Drawing(Account account,int drawingMoney,String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }
    //取钱
    @Override
    public void run() {
        //判断有没有钱
        if (account.money - drawingMoney < 0){
            System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
            return;
        }
        //sleep放大问题的发生性
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //卡内余额  = 余额 - 你取的钱
        account.money = account.money - drawingMoney;
        //你手里的钱
        nowMoney = nowMoney + drawingMoney;
        System.out.println(account.name + "余额为:" + account.money);
        // 手里的钱 Thread.currentThread().getName() = this.getName()
        System.out.println(this.getName() + "手里的钱" + nowMoney);
    }
}

//账户
class Account{
    int money;//余额
    String name;//卡名

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

package mynthread.syn;

/**
* @title: UnsafeBuyTicket
* 不安全的买票
* @Author yuenan.ma
* @Date: 2022/12/4 14:07
* @Version 1.0
*/
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();

        new Thread(station,"a1").start();
        new Thread(station,"a2").start();
        new Thread(station,"a3").start();
    }
}
class BuyTicket implements Runnable{

    //票
    int ticketNums = 10;
    boolean flag = true;//外部停止方式
    @Override
    public void run() {
        //买票
        while (flag){
            buy();
        }
    }
    private void buy(){
        //判断是否有票
        if(ticketNums<=0){
            flag = false;
            return;
        }
        //模拟延时
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //买票
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
    }
}
package mynthread.syn;

import java.util.ArrayList;
import java.util.List;

/**
 * @title: UnsafeList
 * 线程不安全的集合
 * @Author yuenan.ma
 * @Date: 2022/12/4 18:48
 * @Version 1.0
 */
public class UnsafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 1000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(list.size());
    }
}

同步方法
由于我们可以通过private关键字来保证数据对象只能被方法访问﹐所以我们只需要针对方法提出一套机制,这套机制就是synchronized 关键字,它包括两种用法∶synchronized方法和synchronized块.
同步方法: public synchronized void method(int args){}
synchronized方法控制对“对象”的访问,每个对象对应一把锁﹐每个synchronized方法都必须获得调用该方法的对象的锁才能执行﹐否则线程会阻塞,方法一旦执行﹐就独占该锁﹐直到该方法返回才释放锁﹐后面被阻塞的线程才能获得这个锁,继续执行
缺陷:若将一个大的方法申明为synchronized 将会影响效率
![image.png](https://img-blog.csdnimg.cn/img_convert/ed53546aed0317ce2daa1775c598e069.png#averageHue=#eeeeee&clientId=u9afedc4f-7a35-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=444&id=u58bdebdd&margin=[object Object]&name=image.png&originHeight=444&originWidth=674&originalType=binary&ratio=1&rotation=0&showTitle=false&size=56094&status=done&style=none&taskId=u135d936e-c403-4b8d-a57c-c64e1509978&title=&width=674)
同步块
同步块:synchronized (Obj ){ }
Obj称之为同步监视器
Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器﹐因为同步方法的同步监视器就是this ,就是这个对象本身,或者是class【反射中讲解]
同步监视器的执行过程
1.第一个线程访问,锁定同步监视器﹐执行其中代码﹒
2.第二个线程访问,发现同步监视器被锁定﹐无法访问.

  1. 第一个线程访问完毕,解锁同步监视器.

4.第二个线程访问,发现同步监视器没有锁,然后锁定并访问

package mynthread.syn;

/**
 * @title: UnsafeBank
 * 不安全的取钱
 * 两个人去银行取钱
 * 账户
 * @Author yuenan.ma
 * @Date: 2022/12/4 14:20
 * @Version 1.0
 */
public class UnsafeBank {
    public static void main(String[] args) {
        //账户
        Account account = new Account(1000,"结婚基金");
        Drawing you = new Drawing(account,50,"你");
        Drawing girlFriend = new Drawing(account,100,"girlFriend");

        you.start();
        girlFriend.start();
    }
}
//银行:模拟取款
class Drawing extends Thread{

    Account account;//账户
    //取了多少钱
    int drawingMoney;
    //手里有多少钱
    int nowMoney;

    public Drawing(Account account,int drawingMoney,String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }
    //取钱
    //synchronized 默认锁的是this.
    @Override
    public void run() {
        //锁的对象是变化的量,需要增删改的对象
        synchronized(account){

            //判断有没有钱
            if (account.money - drawingMoney < 0){
                System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
                return;
            }
            //sleep放大问题的发生性
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //卡内余额  = 余额 - 你取的钱
            account.money = account.money - drawingMoney;
            //你手里的钱
            nowMoney = nowMoney + drawingMoney;
            System.out.println(account.name + "余额为:" + account.money);
            // 手里的钱 Thread.currentThread().getName() = this.getName()
            System.out.println(this.getName() + "手里的钱" + nowMoney);
        }
    }
}

//账户
class Account{
    int money;//余额
    String name;//卡名

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

package mynthread.syn;

/**
 * @title: UnsafeBuyTicket
 * 不安全的买票
 * @Author yuenan.ma
 * @Date: 2022/12/4 14:07
 * @Version 1.0
 */
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();

        new Thread(station,"a1").start();
        new Thread(station,"a2").start();
        new Thread(station,"a3").start();
    }
}
class BuyTicket implements Runnable{

    //票
    int ticketNums = 10;
    boolean flag = true;//外部停止方式
    @Override
    public void run() {
        //买票
        while (flag){
            buy();
        }
    }
    //synchronized 同步方法,锁的是this
    private synchronized void buy(){
        //判断是否有票
        if(ticketNums<=0){
            flag = false;
            return;
        }
        //模拟延时
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        //买票
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
    }
}
package mynthread.syn;

import java.util.ArrayList;
import java.util.List;

/**
 * @title: UnsafeList
 * 线程不安全的集合
 * @Author yuenan.ma
 * @Date: 2022/12/4 18:48
 * @Version 1.0
 */
public class UnsafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            synchronized (list){
                new Thread(()->{
                    list.add(Thread.currentThread().getName());
                }).start();
            }
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(list.size());
    }
}

package mynthread.syn;

import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @title: TestJUC
 * 测试JUC安全类型的集合
 * @Author yuenan.ma
 * @Date: 2022/12/5 22:43
 * @Version 1.0
 */
public class TestJUC {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();

        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(list.size());
    }
}

死锁
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行﹐而
导致两个或者多个线程都在等待对方释放资源﹐都停止执行的情形.某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题.

package mynthread.syn;

/**
 * @title: DeadLock
 * 死锁:多个是线程互相抱着对方需要的资源,然后形成僵持
 * @Author yuenan.ma
 * @Date: 2022/12/6 19:41
 * @Version 1.0
 */
public class DeadLock {
}
//口红
class Lipstick{

}
//镜子
class Mirror{
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"小马");
        Makeup g2 = new Makeup(1,"小张");
        g1.start();
        g2.start();
    }
}

//化妆
class Makeup extends Thread{
    //需要的资源只有一份,用static来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;//选择
    String grilName;//使用化妆品的人

    Makeup(int choice,String grilName){
        this.choice = choice;
        this.grilName = grilName;
    }

    @Override
    public void run() {
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    //化妆,互相持有对方的锁,就是需要拿到对方的资源
    private void makeup() throws InterruptedException {
        if (choice == 0){
            synchronized (lipstick){//获得口红的锁
                System.out.println(this.grilName + "获得口红的锁");
                Thread.sleep(1000);
                synchronized (mirror){//一秒后想获得镜子
                    System.out.println(this.grilName + "获得镜子的锁");
                }
            }
        }else{
            synchronized (mirror){//获得口红的锁
                System.out.println(this.grilName + "获得镜子的锁");
                Thread.sleep(1000);
                synchronized (lipstick){//一秒后想获得镜子
                    System.out.println(this.grilName + "获得口红的锁");
                    Thread.sleep(2000);
                }
            }
        }
    }
}

package mynthread.syn;

/**
 * @title: DeadLock
 * 死锁:多个是线程互相抱着对方需要的资源,然后形成僵持
 * @Author yuenan.ma
 * @Date: 2022/12/6 19:41
 * @Version 1.0
 */
public class DeadLock {
}
//口红
class Lipstick{

}
//镜子
class Mirror{
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"小马");
        Makeup g2 = new Makeup(1,"小张");
        g1.start();
        g2.start();
    }
}

//化妆
class Makeup extends Thread{
    //需要的资源只有一份,用static来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;//选择
    String grilName;//使用化妆品的人

    Makeup(int choice,String grilName){
        this.choice = choice;
        this.grilName = grilName;
    }

    @Override
    public void run() {
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    //化妆,互相持有对方的锁,就是需要拿到对方的资源
    private void makeup() throws InterruptedException {
        if (choice == 0){
            synchronized (lipstick){//获得口红的锁
                System.out.println(this.grilName + "获得口红的锁");
                Thread.sleep(1000);
            }
            synchronized (mirror){//一秒后想获得镜子
                System.out.println(this.grilName + "获得镜子的锁");
            }
        }else{
            synchronized (mirror){//获得口红的锁
                System.out.println(this.grilName + "获得镜子的锁");
                Thread.sleep(1000);
            }
            synchronized (lipstick){//一秒后想获得镜子
                System.out.println(this.grilName + "获得口红的锁");
                Thread.sleep(2000);
            }
        }
    }
}

死锁避免方法
产生死锁的四个必要条件:

  1. 互斥条件:一个资源每次只能被一个进程使用。
    2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
    3.不剥夺条件︰进程已获得的资源,在末使用完之前,不能强行剥夺。
    4.循环等待条件∶若干进程之间形成一种头尾相接的循环等待资源关系。
    上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件就可以避免死锁发生
    Lock(锁)
    从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
    java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
    ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
package mynthread.syn;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @title: TestLock
 * 测试Lock锁
 * @Author yuenan.ma
 * @Date: 2022/12/6 21:06
 * @Version 1.0
 */
public class TestLock {

    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}
class TestLock2 implements Runnable{

    int ticketNums = 10;
    //可重入锁,定义lock 锁
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){

            try{
                lock.lock();//加锁
                if (ticketNums>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(ticketNums--);
                }else {
                    break;
                }
            }finally {
                //加锁
                lock.unlock();
            }
        }
    }
}

![image.png](https://img-blog.csdnimg.cn/img_convert/dc2ea196be1d018b373ca45c63a13e5a.png#averageHue=#fbfafa&clientId=u798f1a0f-f3de-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=292&id=u235be709&margin=[object Object]&name=image.png&originHeight=292&originWidth=561&originalType=binary&ratio=1&rotation=0&showTitle=false&size=64454&status=done&style=none&taskId=uf80c75e7-f84a-4a64-939a-bf25bebb552&title=&width=561)
synchronized 与Lock 的对比
Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放
Lock只有代码块锁,synchronized有代码块锁和方法锁
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
优先使用顺序:
Lock >同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)

线程通信问题

线程协作
生产着消费者模式

线程通信
应用场景︰生产者和消费者问题
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费.
如果仓库中没有产品﹐则生产者将产品放入仓库﹐否则停止生产并等待﹐直到仓库中的产品被消费者取走为止.
如果仓库中放有产品﹐则消费者可以将产品取走消费﹐否则停止消费并等待,直到仓库中再次放入产品为止.
![image.png](https://img-blog.csdnimg.cn/img_convert/a16353f944e4fedd78f33de2795be1cb.png#averageHue=#fdfdfb&clientId=u798f1a0f-f3de-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=146&id=uab97b67d&margin=[object Object]&name=image.png&originHeight=146&originWidth=325&originalType=binary&ratio=1&rotation=0&showTitle=false&size=23722&status=done&style=none&taskId=ufced6b27-a16e-42a4-bd8b-d68bc82f443&title=&width=325)
线程通信-分析
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件.
对于生产者,没有生产产品之前,要通知消费者等待﹒而生产了产品之后﹐又需要马上通知消费者消费
对于消费者﹐在消费之后﹐要通知生产者已经结束消费﹐需要生产新的产品以供消费.
在生产者消费者问题中,仅有synchronized是不够的
synchronized可阻止并发更新同一个共享资源,实现了同步
synchronized 不能用来实现不同线程之间的消息传递(通信)
线程通信
Java提供了几个方法解决线程之间的通信问题

方法名作用
wait()表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
wait(long timeout)指定等待的毫秒数
notify()唤醒一个处于等待状态的线程
notifyAll()唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常lllegalMonitorStateException
线程通信-分析
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件.
对于生产者,没有生产产品之前﹐要通知消费者等待﹒而生产了产品之后﹐又需要马上通知消费者消费
对于消费者﹐在消费之后﹐要通知生产者已经结束消费﹐需要生产新的产品以供消费.
在生产者消费者问题中,仅有synchronized是不够的
synchronized可阻止并发更新同一个共享资源,实现了同步synchronized不能用来实现不同线程之间的消息传递(通信)
解决方式1
并发协作模型“生产者Ⅰ消费者模式”—>管程法
生产者:负责生产数据的模块(可能是方法,对象﹐线程,进程);
消费者∶负责处理数据的模块(可能是方法﹐对象,线程﹐进程);
缓冲区∶消费者不能直接使用生产者的数据
他们之间有个“缓冲区生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
![image.png](https://img-blog.csdnimg.cn/img_convert/cefd4dccbbae243483b554298751ccae.png#averageHue=#fefefe&clientId=u798f1a0f-f3de-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=169&id=u547146d7&margin=[object Object]&name=image.png&originHeight=169&originWidth=355&originalType=binary&ratio=1&rotation=0&showTitle=false&size=24642&status=done&style=none&taskId=ub3bb30fa-2c3e-4a39-8a93-75563785e50&title=&width=355)

package gaoji;

/**
 * @title: TestPC
 * @Author yuenan.ma
 * 测试生产者消费者模型-->利用缓冲区解决:管程法
 * 生产者,消费者,产品,缓冲区
 * @Date: 2022/12/6 22:47
 * @Version 1.0
 */
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Constomer(container).start();
    }
}
//生产者
class Productor extends Thread{
    SynContainer container;
    public Productor(SynContainer container){//构造器,方便创建对象
        this.container = container;
    }
//    生产

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));
            System.out.println("生产了"+i+"只鸡");
        }
    }
}
//消费者
class Constomer extends Thread{
    SynContainer container;
    public Constomer(SynContainer container){//构造器,方便创建对象
        this.container = container;
    }
//    消费

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了-->"+container.pop().id+"只鸡");
        }
    }
}
//产品
class Chicken{
    int id;//产品编号

    public Chicken(int id) {
        this.id = id;
    }
}
//缓冲区
class SynContainer{
    //需要一个容器大小
    Chicken[] chickens = new Chicken[10];
    int count = 0;
//    生产者放入产品
    public synchronized void push(Chicken chicken){
//        如果容器满了,就需要消费者消费
        if (count == chickens.length){
//            通知消费者消费,生产者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//        如果没有满,我们就需要丢入产品
        chickens[count] = chicken;
        count++;
        //可以通知消费者消费了
        this.notifyAll();
    }
//    消费者消费产品
    public synchronized Chicken pop(){
        //判断能否消费
        if (count == 0 ){
            //等待生产者生产,消费者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
        }
//        如果可以消费
        count--;
        Chicken chicken = chickens[count];

//        吃完了,通知生产者生产
        this.notifyAll();
        return chicken;
    }
}

解决方式2
并发协作模型“生产者/消费者模式”—>信号灯法

package gaoji;

/**
 * @title: TestPc2
 * 测试生产者消费问题2:信号灯法,标志位解决_设置flag
 * @Author yuenan.ma
 * @Date: 2022/12/8 22:38
 * @Version 1.0
 */
public class TestPc2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}
//生产者-->演员
class Player extends Thread{
    TV tv;
    public Player (TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2==0){
                this.tv.play("<抖音记录美好生活>");
            }else {
                this.tv.play("广告一下马上回来");
            }
        }
    }
}

//消费者-->观众
class Watcher extends Thread{
    TV tv;
    public Watcher (TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//产品-->节目
class TV{
//    演员表演,观众等待
//    观众观看,演员等待
    String voice;//表演的节目
    boolean flag = true;
//    表演
    public synchronized void play(String voice){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("演员表演了:"+voice);
//        通知观众观看
        this.notifyAll();//通知唤醒
        this.voice = voice;
        this.flag =! this.flag;
    }
//    观看
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("观看了:"+voice);
//        通知演员表演
        this.notifyAll();//通知唤醒
        this.flag =! this.flag;
    }
}

使用线程池
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理(…)
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止

使用线程池

JDK 5.0起提供了线程池相关API: ExecutorService和Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
void execute(Runnable command)∶执行任务/命令,没有返回值,一般用来执行Runnable
Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
void shutdown():关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

package gaoji;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

/**
 * @title: ThreadNew
 * 回顾总结线程的创建
 * @Author yuenan.ma
 * @Date: 2022/12/11 20:39
 * @Version 1.0
 */
public class ThreadNew {
    public static void main(String[] args) {
        new MyThread1().start();
        new Thread(new MyThread2()).start();

        FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
        new Thread(futureTask).start();
        try {
            Integer integeer = futureTask.get();
            System.out.println(integeer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
//1.继承Thread
class MyThread1 extends Thread{
    @Override
    public void run() {
        System.out.println("MyThread1");
    }
}
//2.实现Runnable 接口
class MyThread2 implements Runnable{

    @Override
    public void run() {
        System.out.println("MyThread2");
    }
}
//3.实现Callable接口
class MyThread3 implements Callable<Integer>{

    @Override
    public Integer call() throws Exception {
        System.out.println("MyThread3");
        return 100;
    }
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值