java基础

java基础语法

注释:不会被执行,给编程人员看的

  • 单行注释://
  • 多行注释:/* */
  • 文档注释:/** */结尾

平时写代码,一定要注意规范

标识符

关键字

Java关键字类别Java关键字关键字含义
访问控制private一种访问控制方式:私用模式,访问控制修饰符,可以应用于类、方法或字段(在类中声明的变量)
protected一种访问控制方式:保护模式,可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符
public一种访问控制方式:共用模式,可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符。
类、方法和变量修饰符abstract表明类或者成员方法具有抽象属性,用于修改类或方法
class声明一个类,用来声明新的Java类
extends表明一个类型是另一个类型的子类型。对于类,可以是另一个类或者抽象类;对于接口,可以是另一个接口
final用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,用来定义常量
implements表明一个类实现了给定的接口
interface接口
native用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的
new用来创建新实例对象
static表明具有静态属性
strictfp用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范
synchronized表明一段代码需要同步执行
transient声明不用序列化的成员域
volatile表明两个或者多个变量必须同步地发生变化
程序控制break提前跳出一个块
continue回到一个块的开始处
return从成员方法中返回数据
do用在do-while循环结构中
while用在循环结构中
if条件语句的引导词
else用在条件语句中,表明当条件不成立时的分支
for一种循环结构的引导词
instanceof用来测试一个对象是否是指定类型的实例对象
switch分支语句结构的引导词
case用在switch语句之中,表示其中的一个分支
default默认,例如:用在switch语句中,表明一个默认的分支。Java8 中也作用于声明接口函数的默认实现
错误处理try尝试一个可能抛出异常的程序块
catch用在异常处理中,用来捕捉异常
throw抛出一个异常
throws声明在当前定义的成员方法中所有需要抛出的异常
包相关import表明要访问指定的类或包
package
基本类型boolean基本数据类型之一,声明布尔类型的关键字
byte基本数据类型之一,字节类型
char基本数据类型之一,字符类型
double基本数据类型之一,双精度浮点数类型
float基本数据类型之一,单精度浮点数类型
int基本数据类型之一,整数类型
long基本数据类型之一,长整数类型
short基本数据类型之一,短整数类型
null空,表示无值,不能将null赋给原始类型(byte、short、int、long、char、float、double、boolean)变量
true真,boolean变量的两个合法值中的一个
false假,boolean变量的两个合法值之一
变量引用super表明当前对象的父类型的引用或者父类型的构造方法
this指向当前实例对象的引用,用于引用当前实例
void声明当前成员方法没有返回值,void可以用作方法的返回类型,以指示该方法不返回值
保留字goto保留关键字,没有具体含义
const保留关键字,没有具体含义,是一个类型修饰符,使用const声明的对象不能更新

标识符:类名,变量名,方法名

  • 所有标识符都以字母,美元符($),或者下划线开头
  • 首字母之后可以是字母,数字,下划线,美元符的组合
  • 不能使用关键字
  • 大小写敏感
  • 可以用中文,但不建议(少用拼音)

数据类型

强类型语言:变量先定义后使用

弱类型语言:JavaScript,Python

Java的数据类型

  • 基本类型

    • 数值类型

      • 整数类型

        • byte占一个字节范围:-128–127
        • short占两个字节范围:-32768–32767
        • int占四个字节
        • long占八个字节
      • 浮点类型

        • float占四个字节

        • double占八个字节

          //整数常用
          int num1=10;
          byte num2=200;//报错,显示换成int
          short num3=30;
          long num=30L;//不加L,默认int
          //小数,浮点数
          float num5=50.1F//不加F默认double
          double num6=3.141592656846515//字符
          char name='A'//一个字符
          //字符串
          string name1="夏习清"//多个字符
          //布尔值
          boolean flag=true;
          boolean flag=false;
          
      • 字符类型char,两个字节

    • boolean类型:占一位,值只有true和false

  • 引用类型

    • 接口
    • 数组

位:计算机内数据存储最小的单位(bit)

字节:byte计算机数据处理的基本单位

1B=8b

字符:计算机使用的字母,数字,字和符号

1bit =1位

1024B=1KB

1024KB=1M

1024M=1G

面试问题

二进制0b

八进制0

十进制

十六进制0x:0-9,A-F

int i=10;//十进制 10
int i1=010;//八进制 8
int i2=0x10;//十六进制 16
//浮点数
//float f1=0.1f;   //0.1
//double d=1.0/10;   //0.1
//f==d? false
float d1=123123f;
float d2=d1+1;
d1==d2? true
//float是大约数,接近但不等于
//double 
银行业务BigDecimal 大数数据类型
字符拓展
char c1='a';
char c2='中';
强制转换
 system.out.print((int)c1);//97
system.out.print((int)c2);//20013
//所有的字符本质还是数字
//编码问题 Unicode,2字节 0-65536
a=97,A=65
//16进制 U0000---UFFFF,u0061=a=97
\表示转义
\b表示退格
\\斜杆
\'单引号
\"双引号
\t 制表符
    system.out.print("hello\tworld")输出中间有空格
\n换行
String
    string sa="hello";
    string sb="hello";
    string sc=new string("hello");
    string sd=new string("hello");
    sa=sb
    sc!=sd
boolean flag=true;
if(flag=true){}
if (flag){}相等
 

类型转换

Java是强类型语言,一些时候需要进行类型转换

不同类型先转换成同一类型在运算

低--------高

byte,short,char->int->long->float->double

小数优先级大于整数

强制转换从高到低

  1. 不能对布尔值进行转换
  2. 不能把对象类型转成不相干类型
  3. 高容量转换成低容量,强制转换
  4. 转换可能存在内存溢出或者精度问题

自动转换从低到高

public class Day3 {
    public static void main(String[] args) {
        int i=128;
        //byte b=i
        byte c=(byte)i;//内存溢出
        double d=i;
        System.out.println(i);//128
        //System.out.println(b);
        System.out.println(c);//-128
        System.out.println(d);//128.0
        System.out.println((int)23.7);//23,精度丢失
        System.out.println((int)-45.89f);//-45
        char c1='a';
        int c2=c1+1;
        System.out.println(c1);//98
        System.out.println((char)c2);//b
        //注意溢出问题
        //当操作数比较大时,注意溢出问题
	//JDK7新特性,数字之间可以用下划线分割
	int money = 10_0000_0000; //10亿,下划线不会被打印出来
	System.out.println(money); //1000000000
	int years = 20;

	int total = money*years;  //数据大,溢出
	System.out.println(total); //-1474836480

	long total2 = money*years; //默认是int,转换前就有溢出问题
	System.out.println(total2); //-1474836480
	
	long total3 = money*(long)years; //先把一个数转Long
    System.out.println(total3); //20000000000

变量

变量:可以变化的量

Java每个变量使用之前必须声明变量类型

//数据类型 变量名=值;例 int a=10;

变量包含:变量名,变量类型,作用域

用,隔开声明的多个相同类型的变量名

注:

  • 每个变量都有类型,可以是基本类型也可以是引用类型
  • 变量名必须是合法的标识符
  • 变量声明是一条完整的语句,必须用;结束

作用域

类变量:加上static(写在类里面)

实例变量:不加static(可以写在类中间)

局部/成员变量:写在方法里

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

默认值

int是0

boolean默认false

string默认是null

除了基本类型,其他都是null

double是0.0

变量声明的位置决定变量作用域

变量作用域确定可在程序中按变量名访问该变量的区域

public class Var {
    public static void main(String[] args) {
        int a=0;//a的作用域是main方法
        for (; a < 4; a++) {
            System.out.println("hello");
        }
        System.out.println(a);
    }
}
public class Var {
    public static void main(String[] args) {
        for (int a=0; a < 4; a++) {//a的作用域是for循环
            System.out.println("hello");
        }
        System.out.println(a);//报错
    }
}
public class Var {
    //属性:成员变量,全局变量
    int num;
    String s;

    //方法
    public void m1(){
        //局部变量:作用域是一个方法,一个语句块
        //a的作用域是for循环
        for (int a=0; a < 4; a++) {
            System.out.println("hello");
        }
        int n=9;
        System.out.println(n);
    }

    public static void main(String[] args) {
        int a=0;//作用域是整个main方法
        for (; a < 4; a++) {
            System.out.println("hello");
        }
        System.out.println(a);
    }
}

类里面的变量是成员变量(全局变量)

方法里面的变量是局部变量

//错误例子分析
public class Test {
    int score1=88;
    int score2=98;
    public void calcAvg(){
        int avg=(score1+score2)/2;
        
    }
    public void showAvg(){
        System.out.println("平均分是:"+avg);
    }
}

在使用局部变量和成员变量时的区别

  1. 作用域不同
    • 局部变量的作用域仅限定义它的方法
    • 成员变量的作用域在整个类内部都是可见的
  2. 初始值不同
    • java会给成员变量一个初始值(默认初始值)
    • java不会给局部变量赋予初始值(局部变量必须初始化后使用)

注意

  1. 在同一个方法中,不允许有同名局部变量
  2. 在不同方法中,可以有同名局部变量
  3. 在同一个类中,成员变量和局部变量同名时,局部变量具有更高的优先级

参数也是变量,是局部变量

public class Test {
    public int num;
    public void calc(int num,int num2){
        System.out.println(num+num2);
    }
    public static void main(String [] args){
        Test test=new Test();
        test.num=10;
        test.calc(11,3);
    }
}
//输出14

常量

常量:不会变的值

关键字:final 常量名=值;

final double PI=3.14;

常量名一般使用大写字符

static,final不区分先后顺序(修饰符)

//修饰符 不存在先后顺序,static可以写final后面
static final doube PI=3.14; //类变量,该类下的全局范围

变量命名原则

所有变量,方法,类名:见名知意

变量和方法:驼峰原则

类成员变量:首字母小写+驼峰原则:monthSalary

局部变量:首字母小写+驼峰原则

常量:大写字母+下划线:MAX_VALUE

类名:首字母大写+驼峰原则:Man,GoodMan

方法名:首字母小写+驼峰原则:run(),runRun()

运算符

算术运算符:+, - ,*, /(整除), % (取余), ++, –

int a=10;
int b=20;
int c=25;
int d=25;
System.out.println(a+b);//30
System.out.println(a-b);//-10
System.out.println(a*b);//200
System.out.println(a/b); //输出是0,需要变成0.5
System.out.println(a/(double)b); //0.5
long a=12312313123123L;
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
System.out.println((double)c+d);//int
//int不能转换成string
//System.out.println((string)  c+d);
// ++自增 --自减 单目运算符
int a = 3;
int b = a++; //b=a,a=a+1 先赋值 即b=3 a=4
int c = ++a; //a=a+1,c=a 先自增 即a=5 c=5

System.out.println(a); //5
System.out.println(b); //3
System.out.println(c); //5

赋值运算符:=

关系运算符:> ,< ,>=, <= ,==, != ,instanceof

        int a=10;
        int b=20;
        int c=21;
        System.out.println(c%a);//21/10=2....1
        System.out.println(a>b);//f
        System.out.println(a<b);//t
        System.out.println(a==b);//f
        System.out.println(a!=b);//t
String name = "张三";
boolean flag = name instanceof String;  //name是String类型的,返回true

逻辑运算符:&&(与),||(或),!(非)

	    boolean a=true;
        boolean b=false;
        System.out.println("a&&b"+(a&&b));//逻辑与,两个都为真,结果为真f
        System.out.println("a||b"+(a||b ));//逻辑或,一个为真,结果为真t
        System.out.println("!(a&&b)"+!(a&&b));//如果为真,结果为假t
        //短路运算
        int c=5;
        boolean d=(c<4)&&(c++<4);
        System.out.println(d);//f
        System.out.println(c);//5

位运算符:&,|,^,~,>>,<<,>>>(了解)

/*
       A=0011 1100
       B=0000 1101
       A&B=0000 1100 两个都是1才得1
       A|B=0011 1101 都是0就是0,否则就是1
       A^B=0011 0001 相同就是0,否则就是1
       ~B=1111 0010 取反
       面试题
       2*8怎么运算最快
       左移<<  *2
       右移>>  /2
       0000 0000 =0;
       0000 0001 =1;
       0000 0010 =2;
       0000 0011 =3;
       0000 0100 =4;
       0000 1000 =8;
       0001 0000 =16;
       System.out.print(2<<3);
       输出16
       位运算优点:效率高
*/

条件运算符:?:

扩展赋值运算符:+=,-=,*=,/=

  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);
        //串在前面直接拼接1020
        System.out.println(a+b+"");
        //串在后面先运算在拼接30
        //幂运算  2^3 2*2*2=8 Math.
        double pow=Math.pow(2,3);
        System.out.println(pow);
	    //三元运算符 x?y:z
        //如果x==true,结果为y,否则结果为z
        int score=80;
        String type=score<60?"不及格":"及格";//结果及格
        System.out.println(type );

扩展面试

//扩展:笔试题 i=5 s=(i++)+(++i)+(i--)+(--i) s=?

int i=5;
int s=(i++)+(++i)+(i--)+(--i);
System.out.println(s); //24

优先级()单目>算术>位移>关系>逻辑>三目>赋值

() ,[], . ,–,++,~,!,*,/ ,%,+ ,-, <<,>>, >>>, < ,<=,>, >=,instanceof,==,!=,&,^,|,&&,||, ? : ,+=,-=,/= ,

包机制

包:为了更好的组织类,Java提供了包机制,防止命名冲突

包名一般利用公司名字倒置来命名

*通配符,导入这个包下所有的类

问题:如何存放两个名字相同的类而不冲突

使用不同的包

包的作用

  1. 允许类组成较小的单元(类似于文件夹),易于找到和使用相应的文件
  2. 有助于实施访问权限控制
  3. 防止命名冲突,区分名字相同的类

两种类型的包

  1. JDK提供基本包
    • java.lang:虚拟机自动引入
    • java.util:提供一些实用类
    • java.io:输入,输出
  2. 使用IDEA创建包
    • 分别创建包和类
    • 创建类的过程中创建类所在的包

包名:小写字母组成,不能以圆点开头或结尾

​ 最好加上唯一的前缀,常用组织倒置的网络域名

​ 后缀部分,不同机构要求不一样

声明包: package 包名;

导入包:import 包名.类名;

使用包的注意事项

  1. 一个类同时引用了两个来自不同包同类名

    • 必须通过完整的类名来区分
  2. 每个包都是独立的,顶层包不会包含子包的类

  3. package和import的顺序是固定的

    • package必须位于第一行(忽略注释行)
    • 只允许有一个package语句
    • 其次是import
    • 接着是类的声明

Javadoc

javaDoc -encoding UTF-8 -charset UTF-8 hello.java

之后查看生成的index.html文件即可

Javadoc:@author作者,@version版本,@since指明最早使用的jdk版本,@param参数,@return返回,@throws异常

流程控制

用户交互scanner

scanner:实现程序和人的交互,用这个类获取用户的输入

Scanner s=new Scanner(System.in);

通过Scanner类的next()和nextLine()方法获取输入的字符串

next:不能输入带有空格的字符串(带有空格,后面的内容不能输出)

nextLine:可以输入带有空格的字符串(空格后面的内容不会舍弃)

关闭scanner.close()

在读取时一般使用hasNext()和hasNextLine()判断是否还有输入的数据

	    Scanner scanner =new Scanner(System.in);
        System.out.println("用next方式接收:");
        //判断用户有没有输入字符串
        if (scanner.hasNext()==true){
            String str=scanner.next();
            System.out.println("输入的内容为"+str);
        }
        //属于IO流的类如果不关闭会一直占用资源,要用完就关
        scanner.close();
    }//输入hello word,输出 hello
 	   Scanner scanner=new Scanner(System.in);
        System.out.println("使用nextLine方式接收数据");
        if(scanner.hasNextLine()){
            String str=scanner.nextLine();
            System.out.println("输出内容为"+str);
        }
        scanner.close();
		}//输入hello word,输出 hello word

//输入数据
		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("输入的不是整数数据!");
        }

练习:输入多个数据,求和和平均数,没输入一个数据用回车确定,
通过输入非数字来结束输入用输出执行结果

        Scanner scanner=new Scanner(System.in);
        //和
        double sum=0;
        int m=0;//输入的个数
        //循环判断是否还有输入,并对里面的数据进行求和
        while (scanner.hasNextDouble()){
            double x=scanner.nextDouble();
            m=m+1;
            sum=sum+x;
            System.out.println("你输入了第"+m+"个数据,当前的和为"+sum);
        }
        System.out.println(m+"个数的和为"+sum);
        System.out.println(m+"个数的平均值为"+sum/m);
        scanner.close();
    }

顺序结构

java的基本结构是顺序结构(除非特殊指明,否知按照顺序一句一句执行),顺序结构师最简单的算法

        System.out.println("hello1");
        System.out.println("hello2");
        System.out.println("hello3");
        System.out.println("hello4");
        System.out.println("hello5");

选择结构

if单选择:if(布尔表达式){//将要执行的语句}

 		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");

if双选择:if(布尔表达式){//将要执行的语句}

​ else(布尔表达式){//将要执行的语句}

		Scanner scanner=new Scanner(System.in);
        System.out.println("请输入成绩:");
        int i=scanner.nextInt();
        if(i>60){
            System.out.println("及格");
        }
        else {
            System.out.println("不及格");
        }

        scanner.close();

if多选择:多种不同的情况

if(表达式){执行内容}

else if(表达式){执行内容}

else{执行内容}

		if(i>60&&i<70){
            System.out.println("及格");
        }
        else if(i>70&&i<80){
            System.out.println("良好");
        }
        else if(i>80&&i<90){
            System.out.println("好");
        }
        else if(i>90&&i<100){
            System.out.println("优秀");
        }
        else {
            System.out.println("不及格");
        }

        scanner.close();
    }

if嵌套选择

if(表达式){}

​ if(表达式){}

​ if(表达式){}

Switch多选择:一般与case连接起来

Switch(expression){

case value://语句

break;

case value://语句

break;

case value://语句

break;

default://语句

}

		Scanner scanner=new Scanner(System.in);
        String str= scanner.nextLine();
        switch (str){
            case "周自珩":
                System.out.println("匹配");
                break;
            case "夏知许":
                System.out.println("亲戚");
                break;
            case "许其琛":
                System.out.println("朋友");
                break;
            case "夏习清":
                System.out.println("自己");
                break;
            default:
                System.out.println("未知");

循环结构

while循环

while(表达式){循环内容}

  • 表达式为真,循环一直走下去
  • 一般都不会让程序一直循环
  • 少部分情况下,需要一直执行,例如某些监听
  • 避免死循环
  • 不满足条件,不执行循环
 		int i=0;
        int sum=0;
        while(i<100){
            i++;
            sum=sum+i;
        }
        System.out.println(sum);

do ……while循环

do{

//代码语句}

while(条件);

  • do……while至少执行一次
  • while先判断后执行,do…while先执行后判断
 int i=0;
        int sum=0;
        do {
            sum=sum+i;
            i++;

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

for循环

for循环支持迭代的通用结构,最有效最灵活的循环结构

练习:

  • 1-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);
    
  • while或for输出1-1000被5整除的数,每行输出3个

    println自带换行

    print不带换行

    		for (int i = 0; i < 1000; i++) {
                if(i%5==0){
                    System.out.print(i+"\t");
    
                }
                if(i%(5*3)==0){
                    System.out.println();
                }
            }
    
  • 打印九九乘法表

		 for(int i=1;i<=9;i++){
            for(int j=1;j<=i;j++){
                System.out.print(j+"*"+i+"="+i*j+"\t");
                if(i==j){
                    System.out.println();
                }
            }
        }

break&continue

break:强制退出循环

continue:终止某次循环,跳出此次循环中尚未执行的语句;接着进行下一次循环

//不输出10的倍数
int i=0;
while(i<100){
    i++;
    if(i%10==0){
        System.out.println();
        continue;
    }
    System.out.println(i);
}
//输出1-9
int i=0;
while(i<100){
    i++;
    if(i%10==0){
        System.out.println();
        break;
    }
    System.out.println(i);
}

练习

  • 打印三角形(5行)

    for (int i = 1; i <= 5; i++) {
            for (int j = 5; j >=i ; j--) {
                System.out.print(" ");
            }
            for (int j = 1; j <=i ; j++) {
                System.out.print("*");
            }
            for (int j = 1; j <i ; j++) {
                System.out.print("*");
            }
            System.out.println();
        }
    }
    

增强for循环

主要遍历数组和集合

for(声明语句:表达式){

//代码语句}

int[] numbers={10,20,30,40,50,60};
    for(int i=0;i<6;i++){
        System.out.println(numbers[i]);
    }
    /*
    for(int x:numbers){
        System.out.println(x);
    }

     */
}

java方法

方法是语句的集合,放在一起执行一个功能

方法是解决一类问题的步骤的有序集合

方法包含于类或对象中

方法在程序中被创建,在其他地方被引用

方法保存:一个方法只完成一个功能,这样有利于后期的扩展

 public static void main(String[] args) {
        int sum =add(1,2);
        System.out.println(sum);
        test();
    }
    //加法
    public static int add(int a,int b){
        return a+b;
    }
    public static void test(){
        System.out.println("hello1");
        System.out.println("hello2");
    }
}

方法:类似其他语言的函数,是一段用来完成特定功能的代码片段

方法定义

修饰符 返回值类型 方法名(参数类型,参数类型){

方法体

……

return 返回值;}

public static int add(int a,int b){
    return a+b;

修饰符:定义了此方法的访问类型

返回值类型:可能会有返回值,没有返回值的话写void

方法名:此方法的名称

参数类型:传递值给参数,实参或形参

  • 实参:调用方法时实际传给方法的数据(上述1,2是实参)
  • 形参:在方法被调用时用于接收外界输入的数据(上述a,b是形参)

方法体:具体语句,定义此方法的功能

方法调用:对象名.方法名 类名.方法

命令行给main传递参数:Java 文件名 参数

方法重载

重载:一个类中,有相同的函数名称,但是形参(参数列表)不同

重载规则

  • 方法名必须相同
  • 参数列表必须不同(个数不同,类型不同,顺序不同)
  • 返回类型可以相同,也可以不同
  • 仅仅返回值类型不同,不能成为方法重载
int max=max(10,20);
    double max1=max(10.0,11.1);
    System.out.println(max);
    System.out.println(max1);
}
public static int max(int a,int b){
    int result=0;
    if(a>b){
        result=a;
    }
    else {
        result=b;
    }
    return result;
}
public static double max(double a,double b){
    double result=0;
    if(a>b){
        result=a;
    }
    else {
        result=b;
    }
    return result;
}

实现简易计算器,分别实现两个整数,三个浮点数的加法运算

public class Calc {
    //加法:两个整数
    public void add(int num1,int num2){
        int sum=num1+num2;
        System.out.println(num1+"+"+num2+"="+sum);

    }

    //加法:两个浮点数
    public void add(double num1,double num2){
        double sum=num1+num2;
        System.out.println(num1+"+"+num2+"="+sum);
    }

    //加法:三个浮点数
    public void add(double num1,double num2,double num3){
        double sum=num1+num2+num3;
        System.out.println(num1+"+"+num2+"+"+num3+"="+sum);
    }

    public static void main(String[] args) {
        Calc calc=new Calc();
        calc.add(4,5);
        calc.add(7.0,8.0);
        calc.add(7.0,8.0,9.0);
    }
}

可变参数

java支持传递同类型的可变参数给一个方法

在方法声明中,在指定参数类型后加一个省略号(…)

一个方法中只能制定一个可变参数,它必须是方法的最后一个参数。

任何普通参数都必须在它之前声明

 day5_5 day=new day5_5();
    test(1,2,3,4,5);
}
public static void test(int x,int... i){
    System.out.println(i[0]);
    //输出是2


 day5_5 day=new day5_5();
        test(1,2,3,4,5);
    }
    public static void test(int... i){
        System.out.println(i[0]);
    }//输出是1

递归

A方法调用A方法

简单程序解决复杂问题

递归:

  • 递归头:什么时候不调用自身。如果没有递归头就陷入死循环了
  • 递归体:什么时候需要调用自身
//阶乘 n! n*(n-1)*...*2*1
public static int f(int n){
    if(n==1) return 1;
    return n*f(n-1); //递归:调用自身
}
public static void main(String[] args) {
    System.out.println(f(5)); //5!= 120
}

练习

书写一个计算器,实现加减乘除

Scanner scanner=new Scanner(System.in);
    int a=scanner.nextInt();
    Scanner scanner0=new Scanner(System.in);
    String c=scanner0.nextLine();
    Scanner scanner1=new Scanner(System.in);
    int b=scanner1.nextInt();
    switch (c){
        case "+":add(a,b);break;
        case "-":jian(a,b);break;
        case "*":cheng(a,b);break;
        case "/":chu(a,b);break;
    }
    scanner.close();
}
public static void add(int i,int j){
    System.out.println(i+j);;
}
public static void jian(int i,int j){
    System.out.println(i-j);;
}
public static void cheng(int i,int j){
    System.out.println(i*j);;
}
public static void chu(int i,double j){
    System.out.println(i/j);;
}

数组

数组简介

数组:相同数据类型的有序集合(基本和引用都可以存储)

通过下标来访问

每个数据称为一个数据元素

数组下标从0开始

使用数组先声明在使用

声明数组:

int [] nums;
int nums2[];

定义数组:int []nums=new int[10];

数组赋值:

nums[0]=1;
nums[1]=2;
nums[2]=3;

声明创建:int [] nums=new int[10];

数组长度:arrays.length

特点:

  • 长度是确定的。一旦数组被创建,大小不可以改变
  • 元素类型必须相同,不能混合
  • 元素可以是任何数据类型包括基本类型和引用类型
  • 数组变量属于引用类型,数组可以看成对象,元素相当于成员变量

数组三种初始化:

  • 动态初始化(包含默认初始化)自己定长度,系统初始化

    int [] nums =new int[5];

  • 静态初始化,自己初始化值,系统决定长度

    int [] nums =new int[]{1,2,3,4,5};

    简写int [] nums={1,2,3,45};

  • 默认初始化:默认值是0

数组边界:合法区间[0,length-1]

越界报错:ArraylndexOutofBounds

数组的使用

取出某个元素

int [] arr=new int[5];
int num1=arr[0];
int num2=arr[1];
int num3=arr[2];
int num4=arr[3];
int num5=arr[4];
System.out.println(num1);
System.out.println(num2);
System.out.println(num3);
System.out.println(num4);
System.out.println(num5);
//输出结果5个0

通过索引重新赋值:

num[0]=1;
num[1]=2;
num[2]=3;

数组名.length 可以获取数组长度或者元素个数

角标越界异常:ArraylndexOutOfBoundsException

数组元素的遍历
  1. 通过角标逐个输出

    int [] arr=new int[5];
    int num1=arr[0];
    int num2=arr[1];
    int num3=arr[2];
    int num4=arr[3];
    int num5=arr[4];
    System.out.println(num1);
    System.out.println(num2);
    System.out.println(num3);
    System.out.println(num4);
    System.out.println(num5);
    
  2. for循环遍历

    int [] arr={1,2,3,4,5}for(int i=0;i<arr.length;i++){
        System.out.println(arr[i]);
    }
    
  3. For-Each循环遍历

int [] arrays={1,2,3,4,5};
//没有下标
for (int array : arrays) {
   System.out.println(array);
}//增强for循环遍历
  1. 数组反向遍历

    int [] arr={1,2,3,4,5}for(int i=arr.length;i>0;i--){
        System.out.println(arr[i]);
    }
    
获取数组最值
  1. 获取最大值

    int [] arr={1,2,3,4,5}int max=0;
    for(int i=1;i<arr.length;i++){
        if(arr[i]>max){
            max=arr[i]
        }
    }
    System.out.println("数组中的最大值是"+max);
    
  2. 获取最小值

    int [] arr={1,2,3,4,5}int min=0;
    for(int i=1;i<arr.length;i++){
        if(arr[i]<min){
            min=arr[i]
        }
    }
    System.out.println("数组中的最小值是"+min);
    
反转数组元素
int [] arr={1,2,3,4,5}for(int i=0;i<arr.length/2;i++){
    int t=arr[i];
    arr[i]=arr[arr.length-1-i];
    arr[arr.length-1-i]=t;
} 

数组作方法入参

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

数组的应用

数组作返回值:反转

int [] arrays={1,2,3,4,5};
    int [] reverse =reverse(arrays);
    printArray(reverse);
}
//反转数组
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;
}

普通的for循环:使用最多,取数据,进行下标操作

int [] arrays={1,2,3,4,5};
    //打印全部数组元素
    for (int i = 0; i < arrays.length; i++) {
        System.out.println(arrays[i]);
    }
    System.out.println("======");
    //计算所有元素的和
    int sum=0;
    for (int i = 0; i < arrays.length; i++) {
        sum=sum+arrays[i];
    }
    System.out.println(sum);

二维数组

二维数组的定义:数据类型[] [] 变量名=new 数据类型[m] [n]

m表示二维数组有多少个一维数组

n表示每个一维数组的元素个数

int[] [] arr=new int [3] [2];

二维数组的长度为3,每个一维数组的长度是2

//动态初始化
int [][] arr=new int [3] [2];
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr[0][0]);
//给二维数组的每个元素重新赋值
arr[0]=new int[2];
arr[1]=new int[2];
arr[2]=new int[2];
//静态初始化
int [][] arr=new int[][]{{2,3,4},{5,2,23},{45,36,14}};
System.out.println(arr[0][2]);
System.out.println(arr[2][2]);
//动态获取
System.out.println(arr[arr.length-1][arr[arr.length-1].length-1]
//简写
int [][] array={{1,2},{2,3},{3,4},{4,5}};                  

int []g,z[];//定义了两个数组,一个一维数组g,一个二维数组z

二维数组的遍历

  1. 逐个输出
  2. for循环
int [][] array={{1,2},{2,3},{3,4},{4,5}};
for (int i = 0; i < array.length; i++) {
    for (int j = 0; j < array[i].length; j++) {
        System.out.println(array[i][j]);
    }
练习
  1. 销售额求和:第一季度:22,66,44;第二季度:77,33,88;第三季度:25,45,65;第四季度:11,66,99

    int [][] arr={{22,66,44}{77,33,88}{25,45,65}{11,66,99}}
    int sum=0;
    for (int i = 0; i < array.length; i++) {
        for (int j = 0; j < array[i].length; j++) {
            sum+=arr[i][j];
        }
    System.out.println("总销售额为"+array[i][j]+"万元")
    
  2. 打印杨辉三角形(行数可以键盘录入)

    1
    1 1
    1 2 1
    1 3 3 1
    1 4 6 4 1
    1 5 10 10 5 1 
    Scanner sc=new Scanner(System.in);
    System.out.println("请输入行数");
    int n=sc.nextInt();
    //定义二维数组存储数据
    int [][] arr=new int [n][n];
    //把每一行的第一列和最后一列设为1
    for (int i = 0; i < arr.length; i++) {
    	arr[i][0]=1;
        arr[i][i]=1;
    }
    //设置中间元素
    or (int i = 2; i < arr.length; i++) {
        for (int j = 1; j < i; j++) {
            arr[i][j]=arr[i-1][j-1]+arr[i-1][j];
        }
    //遍历二维数组
    //for (int i = 0; i < arr.length; i++) {
      //  for (int j = 0; j < arr[i].length; j++) {
        //    System.out.print(arr[i][j]+"\t");
        //}
        //System.out.print();
    //输出正方形,斜线是1,改一下
    for (int i = 0; i < arr.length; i++) {
        for (int j = 0; j <i; j++) {
            System.out.print(arr[i][j]+"\t");
        }   
        System.out.print();
    

多维数组

多维数组:可以看成数组的数组,二维数组是特殊的一维数组,其中每个元素都是一个一维数组

查找思想
  1. 遍历数组按个查找

    int [] arrays={1,50,2,3,4,5,50,20,11,24};
    //查找元素在数组中第一次出现的索引
    int index=getIndexByELE(arr,20);
    System.out.print("该元素第一次在数组中出现的索引是:"+index);
    
    public static int getIndexByELE(int []arr,int ele){
        //遍历数组去查找
        for(int i=0;i<arr.length;i++){
        	if(ele==arr[i]){
                return i;
            }
    }
        return -1;//没有找到返回-1
    }
    
  2. 二分查找

    使用条件:数组元素必须有序

    思想:每次查找中间元素,比较大小就能减少查找次数

    最大索引:arr.length-1

    最小索引:0

    中间索引:(最小索引+最大索引)/2

    查找时那目标元素和中间索引对应元素比大小

    1. 目标等于中间索引,返回中间索引
    2. 目标小于中间索引,改变最大索引,最大索引变成中间索引-1
    3. 目标大于中间索引,改变最小索引,最小索引变成中间索引+1

    移动之后重新计算中间索引

    循环去找while(最小索引<=最大索引)就找到了

    public static void main(String[] args) {
    	int [] a={1,2,5,18,45,56,77};
        int index=getIndexByELE(arr,18);
        System.out.print("该元素出现的位置是"+index);
    
    public static void getIndexByELE(int []arr,int ele){
        int maxIndex=arr.length-1;
        int minIndex=0;
        int centerIndex=(maxIndex+minIndex)/2while(minIndex<=maxIndex){
        if(ele==arr[centerIndex]){
            return centerIndex;
        }else if(ele>arr[centerIndex){
            minIndex=centerIndex+1;
        }else if(ele<arr[centerIndex){
            maxIndex=centerIndex-1;
        }
        //重新计算中间索引
        centerIndex=(maxIndex+minIndex)/2
    	} 
        return -1;
    }
    

Arrays类

数组工具类:java.util.Arrays

八大排序

排序:把无序序列同归某种方式变成一个有序序列

  • 冒泡排序

    数组元素两两比较,交换位置,大元素往后放,经过一轮比较后,最大的元素就会出现在最大索引处

    2469805713

    第一轮比较之后

    2469578013
    2469571380

    第二轮比较之后

    2457691380
    2457136980

    第三轮比较

    2413576980

    第四轮比较

    1324576980

    规律:数组有5个元素,经过了4轮比较

    比较了4次,比较了3次,比较了2次,比较了1次

    public static void main(String[] args) {
    	int [] arr={24,69,80,57,13};
        //第一轮比较,数组长度-1
        for (int i = 0; i < arr.length-1; i++) {
            if(arr[i]>arr[i+1]){
                int t=arr[i];
                arr[i]=arr[i+1];
                arr[i+1]=t;
            }
        //第二轮比较,数组长度-2
        for (int i = 0; i < arr.length-1-1; i++) {
            if(arr[i]>arr[i+1]){
                int t=arr[i];
                arr[i]=arr[i+1];
                arr[i+1]=t;
            }    
        //第三轮比较,数组长度-3
        for (int i = 0; i < arr.length-1-1-1; i++) {
            if(arr[i]>arr[i+1]){
                int t=arr[i];
                arr[i]=arr[i+1];
                arr[i+1]=t;
            } 
        //第四轮比较,数组长度-4
        for (int i = 0; i < arr.length-1-1-1-1; i++) {
            if(arr[i]>arr[i+1]){
                int t=arr[i];
                arr[i]=arr[i+1];
                arr[i+1]=t;
            }
        }
    //比较繁琐,可以进行优化
    

    优化之后的代码

    public static void main(String[] args) {
    	int [] arr={24,69,80,57,13};
        for (int i = 0; i < arr.length-1; i++)
            for (int j = 0; j < arr.length-1-j; j++){
                if(arr[i]>arr[i+1]){
                	int t=arr[i];
                	arr[i]=arr[i+1];
                	arr[i+1]=t;
            } 
            }         
    }
    
    1. 代码比较简单,两层循环,外层冒泡轮数,里层依次比较

    2. 时间复杂度O(n^2)

      int [] a={1,5,2,18,88,56,33};
          int [] sort=sort(a);//返回一个排序后的数组
          System.out.println(Arrays.toString(sort));
          //冒泡排序
          /*
          1.数组中相邻的两个元素,如果第一个大于第二个,就交换位置
          2.每一次比较都会产生一个最大数或者最小数
          3.下一轮就可以减少一次排序
          4.依次循环,直到结束
           */
      }
      public static int[] sort(int[] array){
          int temp=0;
          //外层循环,判断走多少次
          for (int i = 0; i < array.length-1; i++) {
              //内层循环,比较,如果第一大一第二,交换位置。三个元素
              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;
                  }
              }
          }
          return array;
      }
      

    冒泡会有很多不必要的比较,为了节省时间,进行优化

    int [] a={1,5,2,18,88,56,33};
            int [] sort=sort(a);//返回一个排序后的数组
            System.out.println(Arrays.toString(sort));
            //冒泡排序
            /*
            1.数组中相邻的两个元素,如果第一个大于第二个,就交换位置
            2.每一次比较都会产生一个最大数或者最小数
            3.下一轮就可以减少一次排序
            4.依次循环,直到结束
             */
        }
        public static int[] sort(int[] array){
            int temp=0;
            //外层循环,判断走多少次
            for (int i = 0; i < array.length-1; i++) {
                boolean flag=false;//减少没有意义的比较
                //内层循环,比较,如果第一大一第二,交换位置。三个元素
                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;
        }
    }
    
  • 选择排序

    原理:从0开始,依次和后面的元素进行比较,晓得元素往前放,经过一轮比较后,最小的元素就出现在最小索引处

    int [] arr={24,69,80,57,13};

    原数组

    2469805713

    第一轮比较:从0开始

    1369805724

    第二轮比较:从1开始

    1357806924
    1324806957

    第三轮比较:从2开始

    1324698057
    1324578069

    第四轮比较:从3开始

    1324576980

    总结规律:数组中有5个元素,经过了4轮比较

    比较了4次,比较了3次,比较了2次,比较了1次

    public static void main(String[] args) {
    	int [] arr={24,69,80,57,13};
        //第一轮比较:从0开始
        int index=0;
        for (int i = 1; i < arr.length; i++){
            if(arr[index]>arr[i]){
            int t=arr[index];
            arr[index]=arr[i];
            arr[i]=t;    
            }
        }
        //第二轮比较:从1开始
         int index=1;
        for (int i = 1+index; i < arr.length; i++){
            if(arr[index]>arr[i]){
            int t=arr[index];
            arr[index]=arr[i];
            arr[i]=t;    
            }
        }
        //第三轮比较:从2开始
         int index=2;
        for (int i = 1+index; i < arr.length; i++){
            if(arr[index]>arr[i]){
            int t=arr[index];
            arr[index]=arr[i];
            arr[i]=t;    
            }
        }
        //第四轮比较:从3开始
        int index=3;
        for (int i = 1+index; i < arr.length; i++){
            if(arr[index]>arr[i]){
            int t=arr[index];
            arr[index]=arr[i];
            arr[i]=t;    
            }
        }
        //繁琐,可以进行优化
        
    

    优化后的代码:

    public static void main(String[] args) {
    	int [] arr={24,69,80,57,13};
        for (int index = 0; index < arr.length-1; index++){
            for (int i = 1+index; i < arr.length; i++){
                if(arr[index]>arr[i]){
            		int t=arr[index];
            		arr[index]=arr[i];
            		arr[i]=t;    
            }
        } 
    }
    	System.out.println(Arrays.toString(arr));
    }
    
  • 插入排序

    原理:最简单的排序,讲一个记录插入到一个长度为m的有序列表中,使之仍保持有序

    原始数据:49,38,65,97,76,13,27

    [49],38,65,97,76,13,27,从1索引开始插入

    [38,49],65,97,76,13,27

    [38,49,65],97,76,13,27

    [38,49,65,97],76,13,27

    [38,49,65,76,97],13,27

    [13,38,49,65,76,97],27

    [13,27,38,49,65,76,97]

    public static void main(String[] args) {
    	int [] arr={3,2,1,0,10,20,30,7,8};
        //外层循环定义轮次
        for (int i = 1; i < arr.length; i++){
            //里层循环进行插入比较
            int j=i;
            while(j>0&&arr[j]<arr[j-1]){
                int t=arr[j];
                arr[j]=arr[j-1];
                arr[j-1]=t;
                j--;
            }
        }
        System.out.println(Arrays.toString(arr));
    }
    

    或者换成

    public static void main(String[] args) {
    	int [] arr={3,2,1,0,10,20,30,7,8};
        for (int i = 1; i < arr.length; i++){
            for(int j=i;j>0;j--){
               if arr[j]<arr[j-1]){
                	int t=arr[j];
                	arr[j]=arr[j-1];
                	arr[j-1]=t;
                	j--; 
               }
            }
        }
        System.out.println(Arrays.toString(arr));
    }
    

    值交换方法

    public static void swapValue(int []arr,int i,int j){
        int t=arr[i];
        arr[i]=arr[j];
        arr[j]=t;
    }
    
  • 希尔排序或者缩小增量排序

    思想:将原表按照增量ht分组,每个子文件按照直接插入的方法排序,同样,用下一个增量ht/2将文件再分为子文件,在直接插入排序,直到ht=1时整个文件排好序

    关键:选择合适的增量

    原数组:46,55,13,42,17,94,05,70

    步长=4(46,17)(55,94)(13,05)(42,70)

    第一趟结果:17,55,05,42,46,94,13,70

    步长=2(17,05,46,13)(55,42,94,70)

    第二趟结果:05,42,13,55,17,70,46,94

    步长=1

    第三趟结果:05,13,17,42,46,55,70,94

    public static void main(String[] args) {
        //对插入排序的优化
        //直接插入排序就是增量唯一的希尔排序
    	int [] arr={46,55,13,42,17,94,05,70};
        shelSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    //第一轮
    public static void shellSort(int []arr){
        int h=4;
        for (int i = h; i < arr.length; i++){
            for(int j=i;j>h-1;j-=h){
               if arr[j]<arr[j-h]){
                	swapValue(arr,j,j-h); 
               }
            }
        }
    //第二轮
        int h=2;
        for (int i = h; i < arr.length; i++){
            for(int j=i;j>h-1;j-=h){
               if arr[j]<arr[j-h]){
                	swapValue(arr,j,j-h); 
               }
            }
        }
    //第三轮
        int h=1;
        for (int i = h; i < arr.length; i++){
            for(int j=i;j>h-1;j-=h){
               if arr[j]<arr[j-h]){
                	swapValue(arr,j,j-h); 
               }
            }
        }
    }
    public static void swapValue(int []arr,int i,int j){
        int t=arr[i];
        arr[i]=arr[j];
        arr[j]=t;
    }
    }
    //繁琐,可以优化
    

    优化之后

    public static void main(String[] args) {
        //对插入排序的优化
        //直接插入排序就是增量唯一的希尔排序
        //合理选取增量
        //第一次增量选取数组长度的一半
    	int [] arr={46,55,13,42,17,94,05,70};
        shelSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    
    public static void shellSort(int []arr,int i,int j){
        for(int h=arr.length/2;h>0;h/=2){
            for (int i = h; i < arr.length; i++){
            for(int j=i;j>h-1;j-=h){
               if arr[j]<arr[j-h]){
                	swapValue(arr,j,j-h); 
               }
            }
        }
        }
    }
     public static void swapValue(int []arr,int i,int j){
        int t=arr[i];
        arr[i]=arr[j];
        arr[j]=t;
    }
    

    增量选取

    //第一次选择选取数组长度的一半还不是很好
    //我们可以使用一种序列:克努特序列
    int h=1;
    h=h*3+1;
    //根据克努特数列选取第一次增量
    int jinage=1;
    while(jiange<=arr.length/3){
        jiange=jiange*3+1;
    }
    
    最后优化
    public static void shellSort(int []arr,int i,int j){
        int jinage=1;
    	while(jiange<=arr.length/3){
        jiange=jiange*3+1;
    	}
        for(int h=jiange;h>0;h=(h-1)/3){
            for (int i = h; i < arr.length; i++){
            for(int j=i;j>h-1;j-=h){
               if arr[j]<arr[j-h]){
                	swapValue(arr,j,j-h); 
               }
            }
        }
        }
    }    
    
  • 快速排序

    思想: 分治法:比大小,在分区

    1. 从数组中取出一个数,作为基准数
    2. 分区:将比这个数大或者等于的数放在右边小于它的数放在左边
    3. 在对左右区间重复第二步,知道各区间只有一个数

元素5391672408
坑位坑位1坑位3坑位5坑位7坑位6坑位4坑位2
0341257698
public class QuickSortUtils{
   public static void quickSort(int []arr,int start,int end){
       if(start<end){
           int index=getIndex(arr,start,end);
           quickSort(arr,start,index-1);
           quickSort(arr,index+1,end);
       }
   }
   public static int getIndex(int[] arr,int start,int end){
       int i=start;
       int j=end;
       int x=arr[i];
       while(i<j){
           while(i<j&&arr[j]>=x){
               j--;
           }
           if(i<j){
               arr[i]=arr[j];
               i++;
           }
           while(i<j&&arr[j]<x){
               i++;
           }
           if(i<j){
               arr[j]=arr[i];
               j--;
           }
       }
       arr[i]=x;
       return 0;
   }
} 
  • 归并排序

    利用归并思想实现排序

    public static void main(String [] args){
        int [] arr={4,5,7,8,1,2,3,6};
        //拆分
        chaifen(arr,0,arr.length-1);
        //归并
        guiBing(arr,0,3,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }
    public static void cahifen(int[] arr,int startIndex,int endIndex){
        //计算中间索引
        int centerIndex=(startIndex+endIndex)/2;
        if(startIndex<endIndex){
            chaifen(arr,startIndex,centerIndex);
            chaifenarr(arr,centerIndex+1,endIndex);
            guiBing(arr,startIndex,centerIndex);
        }
    }
        
    public static void guiBing(int [] arr,int startIndex,int centerIndex,int enIndex){
        //定义一个临时数组
        int [] tempArr=new int[enIndex-startIndex+1];
        //定义左边数组的起始索引
        int i=startIndex;
        //定义右边的起始索引
        int j=centerIndex+1;
        //定义临时数组的起始索引
        intindex=0;
        //比较左右两个数组的元素大小,往临时数组中方
        while (i<=centerIndex&&j<=enIndex){
            if(arr[i]<=arr[j]){
                tempArr[index]=arr[i]; 
                i++;
            }else{
                tempArr[index]=arr[j];
                j++;
            }
            index++;
        }
        //处理剩余数组
        while(i<=centerIndex){
            tempArr[index]=arr[i]; 
            i++;
            index++;
        }
        while(j<=centerIndex){
            tempArr[index]=arr[j]; 
            j++;
            index++;
        }
        //System.out.println(Arrays.toString(tempArr));
        //把临时数组中元素娶到原数组中
        for(int k=0;k<tempArr.length;k++){
            arr[k+startIndex]=tempArr[k];
        }
    }
    
  • 基数排序

    不需要进行关键字的比较,只需要对关键字进行分配与手机两种操作即可

    public class void main(Sting[] args){
        int [] arr={2,1,5,21,31,444,23,33,47,10,903,124,987,100};
        //得确定排序轮次
        //获取数组最大值
        //int max=getMax(arr);
        //基础排序
        sortArray(arr);
        
        //输出排序后的数组
        System.out.println(Arrays.toString(arr));
        
    }
    private static void sortArray(int [] arr){
        //定义二维数组,放10个桶
        int[][] tempArr=new int[10][arr.length];
        //定义一个统计数组
        int counts=new int[10];
        int max=getMax(arr);
        int len=String.valueOf(max).length();
        //循环轮次
        for(int i=1;i<len;i++,n*=10){
            for(int j=0;j<arr.length;j++){
                //获取每个位上的数字
                int ys=arr[j]/n%10;
                tempArr[ys][counts[ys]++]=arr[j];  
            }
            //取出桶中元素
            int index=0;
            for (int k=0;k<counts.length;k++){
                if(counts[k]!=0){
                    for(int h=0;h<counts[k];h++){
                        //从桶中取出元素放回原数组
                        arr[index]=tempArr[k][h];
                        index++;  
                    }
                    counts[k]=0;//清楚上一次统计的个数
                    
                }
            }
            
        }
    }
    
    private static int getMax(int[] arr){
        int max=arr[0];
        for(int i=1;i<arr.length;i++){
            if(arr[i]>max){
                max=arr[i];
            }
        }
    }
    
  • 堆排序(一种选择排序)

    思想:

    1. 江代排序的序列构成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点
    2. 将其与末尾元素进行交换,此时末尾就是最大值
    3. 然后将剩余的n-1个元素重新构造成一个堆,这样就会得到n个元素的次小值
    4. 如此反复执行没变能得到一个有序序列了

大顶堆:父节点大于下面两个子节点(升序)arr[i]>=arr[2i+1]&&arr[i]>=arr[2i+2]

小顶堆:父节点小于下面两个子节点(降序)

arr[i]<=arr[2i+1]&&arr[i]<=arr[2i+2]

public class void main(Sting[] args){
    //定义一个数组
    int [] arr={1,0,6,7,2,3,4};
    //调整成大顶堆
    //开始调整的位置
    int startIndex=(arr.length-1)/2;
    //循环开始调整
    for(int i=startIndex;i>=0;i--){
        toMaxHeap(arr,arr.length,i);
    }
    System.out.println(Arrays.toString(arr));
    //数组已经变成了大顶堆,把根元素和最后元素进行交换
    for(int i=arr.length-1;i>0;i--){
        //进行调换
        int t =arr[0];
        arr[0]=arr[i];
        arr[i]=t;
        //换完之后,再把剩余元素调成大顶堆
        toMaxHeap(arr,i,0);
    }
    System.out.println(Arrays.toString(arr));
}
//调整数组,调整元素个数,从哪里开始调整
public static void toMaxHeap(int [] arr,int size,int index){
    //获取左右字节的索引
    int leftNodeIndex=index*2+1;
    int rightNodeIndex=index*2+2;
    //查找最大节点所对应的索引
    int maxIndex=index;
    if(arr[leftNodeIndex]>arr[maxIndex]&&leftNodeIndex<size){
        maxIndex=leftNodeIndex;
    }
    if(arr[rightNodeIndex]>arr[maxIndex&&rightNodeIndex<size]){
        maxIndex=rightNodeIndex;
    }
    //调换位置
    if(maxIndex!=index){
        int t=arr[maxIndex];
        arr[maxIndex]=arr[index];
        arr[index]=t;
        //调换完之后可能会影响到下面的子树不是大顶堆,需要再次调换
        toMaxHeap(arr,size,maxIndex);
    }
}

稀疏数组

需求:编写五子棋,续盘和存储

使用条件:

  • 数组中大部分元素为0或者同一个值是,可以使用稀疏数组

处理方式:

  • 记录一共有几行几列,有多少不同值
  • 把具有不同值的元素和行列值记录在一个小规模的数组中,从而压缩程序的规模
package arry;
public class day66 {
    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();
        }
        //转换成稀疏数组来保存
        //获取有效值的个数
        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);
        //创建一个稀疏数组
        int [][] array2=new int [sum+1][3];
        array2[0][0]=11;
        array2[0][1]=11;
        array2[0][2]=11;
        //遍历二维数组,将非零的值存到稀疏数组中
        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("还原");
        //先读取稀疏数组
        int[][]array3=new int[array2[0][0]][array2[0][2]];
        //给其中的元素还原它的值
        for (int i = 1; i < array2.length; i++) {
            array3[array2[i][0]][array2[i][1]]= array2[i][2];
          //打印
        }
        System.out.println("输出原始的数组");
        for(int[] ints:array1){
            for(int anInt:ints){
                System.out.print(anInt+"\t");
            }
            System.out.println();
        }

    }
}

面向对象编程

软件开发周期:需求分析,软件设计,软件开发,软件测试,软件部署和维护

常用的开发方法:

  • 结构化开发
    1. 面向功能划分软件结构
    2. 自顶而下
    3. 最小的子系统是方法
    4. 制约了软件的可维护性和可扩展性
  • 面向对象开发(OOP)
    1. 把软件系统看成各种对象的集合
    2. 系统结构较稳定
    3. 子系统相对独立
    4. 软件可重用性,可维护性和可扩展性强

属性+方法=类

面向对象编程的本质:以类的方式组织代码,以对象的组织数据

三大特性:

  1. 封装
  2. 继承:子类继承父类
  3. 多态

类等同于类别,对象等同于个体(某一个类别下的具体的个体)

属性:对象具有的各种特征

属性值:每个对象的每个属性都拥有特定的值

属性一般可以相同,属性值一般不同

方法:对象执行的操作

对象:用来描述客观事物的一个实体,由一组属性和方法构成

可以从不同的对象中抽取出来相同属性和方法

总述类:具有相同属性和方法的一组对象的集合,类是对象的抽象,对象是类的具体描述

break和return的区别

break:跳出Switch,结束循环

return:结束方法,返回一个结果

定义类

Java程序都以类class为组织单元

关键字class定义自定义的数据类型

方法是完成某一功能

类的模版

public class 类名{
//定义属性部分
属性1类型 属性1;   成员变量
属性2类型 属性2;
······
//定义方法部分
方法1;           成员方法    
方法2;
······
}

命名规范:

  1. 类名首字母大写
  2. 方法和变量:驼峰原则

例子

public class Person {
    //属性(成员变量)
    String name;
    int age;
    char sex;
    //方法(成员方法)
    public void eat(){
        System.out.println("我吃饭");
    }
    public void drink(){
        System.out.println("我喝水");
    }
    public void showInfo(){
        System.out.println("我叫"+name);
    }
}


public class School {
    //属性(成员变量)
    String schoolName;
    int classNum;
    int stuNum;
    //方法
    public void shpuSchoolInfo(){
        System.out.println("学校名"+schoolName);
    }
}

可以通过类图来描述类

  1. 用于分析和设计类

  2. 容易理解

    Dog类名
    +health:int属性名:类型+:public
    +name:String-:private
    -love:int
    +print():void方法名(参数类型):返回值类型

创建和使用对象

创建对象:

​ 类名 对象名 =new 类名();

引用对象成员:使用 . 进行以下操作

​ 引用类的属性:对象名.属性

​ 引用类的方法:对象名.方法名()

public class TestPerson {
    public static void main(String[] args) {
        //创建人类对象: 类名 对象名=new 类名();
        Person zhangSan=new Person();
        //调用对象的成员变量和方法
        zhangSan.name="张三";
        zhangSan.age=18;
        zhangSan.sex='男';
        zhangSan.showInfo();
        zhangSan.eat();
        zhangSan.drink();
    }
}

类和对象练习

编写学生类,输出学生具体信息

编写教师类,输出教师具体信息

//学生类
public class Student{
    //属性有姓名,年龄,班级,爱好
    String name;
    int age;
    String className;
    String favor;
    //方法:输出个人信息
    public void showInfo(){
        System.out.println(name);
        System.out.println("年龄"+age);
        System.out.println("就读于"+className);
        System.out.println("爱好"+favor);
    }
}
//教师类
public class Teacher{
    //属性:姓名,专业,教授课程,教龄
    String name;
    String major;
    String subject;
    int teaAge;
    //方法:输出个人信息
    public void showInfo(){
        System.out.println(name);
        System.out.println("专业方向"+major);
        System.out.println("教授课程"+subject);
        System.out.println("教龄"+teaAge);
    }
}
//创建学生和教师对象
public static void main(String[] args){
    //创建学生对象
    Student student=new Student();
    student.age=19;
    student.cassName="s1";
    student.name="夏习清";
    student.favor="画画";
    student.shouInfo();
    //创建教师对象
    Teacher teacher=new Teacher();
    teacher.major="计算机";
    teacher.name="陈老师";
    teacher.subject="Java oop";
    teacher.teaAge=5;
    teacher.shouInfo();
}

定义方法

方法定义:

  1. 修饰符
  2. 返回类型
  3. 方法名:注意规范,见名知意
  4. 参数列表:参数类型,参数名
  5. 抛出异常

修饰符 返回值类型 方法名(){

方法体

}

没有返回值写void,有返回值写返回值类型即可

return返回一个和返回值类型一致的值

return只返回一个值

return作用:返回值,跳出方法

方法之间允许互相调用

方法调用:递归

  1. 静态方法 static 类名.方法名

  2. 非静态方法,实例化这个类 new 类名.方法 (对象类型 对象名字=对象值)

    • 一个是静态一个不是静态,不能调用,都是静态可以
    • 一个静态一个动态不能调用,类实例化之后可以
  3. 形参和实参

  4. 值传递和引用传递

     int a=1;
        System.out.println(a);
        day2.change(a);
        System.out.println(a);
    }
    public static void change(int a){
        a=10;
    }
    

    引用传递可以改变属性

    public static void main(String[] args) {
            Person person=new Person();
            System.out.println(person.name);
            day3.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;//null
    
    }
    

类的方法练习

  1. 需求:一个景区根据游人的年龄收取不同价格的门票。请编写游人类,根据年龄段决定能够购买门票价格的输出
public class Visitor {
    String name;
    int age;
    //方法:根据年龄判断门票价格
    //18-60  20元;>60 <10  免费,10-18 半价
    public void showPrice(){
        if(age>0&&age<=10){
            System.out.println(name+"的年龄为"+age+"岁:门票免费\n");
        }
        else if(age<18){
            System.out.println(name+"的年龄为"+age+"岁:门票10元\n");
        }
        else if(age<60){
            System.out.println(name+"的年龄为"+age+"岁:门票20元\n");
        }
        else {
            System.out.println(name+"的年龄为"+age+"岁:门票免费\n");
        }
        
public class TestVisitor {
    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        Visitor v=new Visitor();
        boolean flag=true;//输入姓名不是n就循环,输入n就退出
        do {
            System.out.println("请输入姓名");
            String name=input.next();
            if(!name.equals("n")) {
                v.name = name;//将用户输入的姓名赋值给V对象的name属性
                System.out.println("请输入年龄");
                int age = input.nextInt();
                v.age = age;//将用户输入的年龄赋值给V对象的age属性
                v.showPrice();
            }
            else {
                flag=false;
            }
        } while (flag);
        System.out.println("退出程序!");
    }
}          
  1. 模拟实现用户密码管理:输入旧的用户名和密码,如果正确,方法有权限更新;从键盘获取新的密码,进行更新
//管理员类
public class Admin {
    //属性:用户名,密码
    static String userName;
    static String passWord;

    //方法:输出管理员信息
    public void shouInfo(){
        System.out.println("管理员用户名为:"+userName+";密码为:"+passWord);
    }
}


public class TestAdmin {
    public static void main(String[] args) {
    Scanner input=new Scanner(System.in);
    //管理员信息
    Admin admin=new Admin();
    admin.userName="admin000";
    admin.passWord="123456";

    //修改管理员密码
        System.out.print("请输入用户名:");
        String name=input.next();
        System.out.println("请输入密码:");
        String pwd=input.next();
        if(admin.userName.equals(name)&&admin.passWord.equals(pwd)){
            System.out.println("\n请输入新密码:");
            admin.passWord=input.next();
            System.out.println("修改密码成,您的新密码为"+admin.passWord);
        }else {
            System.out.println("用户名和密码不匹配,没有权限修改信息");
        }
}
}

带参方法

public void show(){}

无参方法

public int show(int i ){}

带参方法

定义带参方法 :访问修饰符 返回类型 方法名 (形式参数列表){

方法体}

调用参数方法

对象名.方法名(参数1,参数2…参数n) 实参列表

实参类型一定和形参类型一致

//榨汁例子
public class ZhaZhiJi {
    //属性:样色,价格
    String color;
    double price;

    //方法:输出信息
    public void shouInfo(){
        System.out.println("这是一台"+color+"的榨汁机,价格为"+price);
    }
    //榨汁前提:提供水果(声明参数:参数类型  参数名)
    public void ZhaZhi(String fruit,int num){
        System.out.println(num+"杯"+fruit+ "汁。");
    }
}

public class TestZhaZhiJi {
    public static void main(String[] args) {
        ZhaZhiJi ji=new ZhaZhiJi();
        ji.color="白色";
        ji.price=243.2;
        ji.shouInfo();
        //ji.ZhaZhi("苹果",6);
        //一直修改太麻烦,改成键盘录入
        Scanner input=new Scanner(System.in);
        System.out.println("你想要什么果汁?");
        String fruit=input.next();
        System.out.println("你想要几杯果汁?");
        int num=input.nextInt();
        //实际参数
        ji.ZhaZhi(fruit,num);
    }

}

循环实现榨汁

public class TestZhaZhiJi {
    public static void main(String[] args) {
        ZhaZhiJi ji=new ZhaZhiJi();
        ji.color="白色";
        ji.price=243.2;
        ji.shouInfo();
        //ji.ZhaZhi("苹果",6);
        //一直修改太麻烦,改成键盘录入
        Scanner input=new Scanner(System.in);
        System.out.println("你想要什么果汁?");
        String fruit=input.next();
        if(!fruit.equals("n")) {
            System.out.println("你想要几杯果汁?");
            int num = input.nextInt();
            //实际参数
            ji.ZhaZhi(fruit, num);
        }
        else {
            System.out.println("退出程序!");
        }
    }
}

方法定义处的参数叫形式参数,方法调用处传的值叫实际参数

带参方法,参数个数可以是一个,也可以是多个,多个参数之间用,隔开

带参方法,参数名字可以随意取,符合命名规则即可

形参和实参名字可以不一样,但数据类型必须一致,个数必须一致,顺序也要一致

方法有没有参数和方法有没有返回值是两个情况,毫无关系

带参方法的应用

增加学生姓名

在保存多个学生姓名的数组中,制定查找区间,查找某个学生姓名是否查询成功

//学员管理系统
public class StudentMgr {
    //属性:存放学生姓名的数组
    String[] names = new String[30];
    //方法:增加学生姓名
    public void addName(String name) {
        for (int i = 0; i < names.length; i++) {
            if (names[i] == null) {
                names[i] = name;
                break;            }
        }
    }
    //方法:固定区间,查找学生姓名
    //start开始位置,end结束位置,name查找的学生名字
    public boolean searchName(int start, int end, String name) {
        boolean flag = false;//true是找到了
        for (int i = start - 1; i < end; i++) {
            if (name.equals(names[i])) {
                flag = true;
                break;
            }
        }
        return flag;
    }

    //显示本班学生信息
    public void showNames() {
        System.out.println("本班学生列表");
        for (int i = 0; i < names.length; i++) {
            if (names[i] != null) {
                System.out.println(names[i] + "\t");
            }
        }
    }
}
public class TestStudentMgr {
    public static void main(String[] args) {
        StudentMgr mgr=new StudentMgr();
        Scanner input=new Scanner(System.in);
        for (int i = 0; i < 5; i++) {
            System.out.println("请输入第"+(i+1)+"个学生姓名");
            String name=input.next();
            mgr.addName(name);
        }
        mgr.showNames();
        //查找某个学生信息
        System.out.println("请输入开始查找的位置");
        int start=input.nextInt();
        System.out.println("请输入结束查找的位置");
        int end=input.nextInt();
        System.out.println("请输入要查找的学生姓名");
        String findName=input.next();
        boolean flag=mgr.searchName(start,end,findName);
        if(flag){
            System.out.println("恭喜你,查到该学生");
        }else {
            System.out.println("对不起,没在该范围内查到该学生");
        }
    }
}

方法传参——基本&引用数据类型

  1. 掌握基本&引用传递时候的区别
public class Test {
    public void calc1(int num){
        num=num+1;
        System.out.println(num);
    }
    public void calc2(Student student){
        student.age=student.age+1;
    }
}

public class Student {
    String name;
    int age;
    int score;
    public void showInfo(){
        System.out.println(name+"的成绩是:"+score);
    }
}

public class Demo {
    public static void main(String[] args) {
        Test test=new Test();
        int n=8;
        test.calc1(n);//参数类型为基本数据类型
       //复制一份进行传递
        Student stu=new Student();
        stu.age=18;
        test.calc2(stu);//参数类型为引用类型,自定义数据类型(数组,接口)
        //传递地址,不进行复制
        System.out.println(n+"---"+stu.age);
    }
}
//num变成了9,,n没变还是8,,stu.age变成了19

总结:基本数据类型,传递的是变量的值,改变一个变量的值不会影响另一个变量的值。引用数据类型(类,接口,数组),赋值是把原对象的引用(可以理解为内存地址)传递给另一个引用。

方法传参——对象数组

  1. 对象数组作为参数时如何应用

使用学员对象数组作为参数,实现学员成绩修改

​ 如果成绩小于60分,将其成绩提高2分

public class Student {
    String name;
    int age;
    int score;
    public void showInfo(){
        System.out.println(name+"的成绩是:"+score);
    }
}
public class ModifyScore {
    //修改小于60分的学生的成绩
    public void modifyStuScore(Student [] stus){
        for (int i = 0; i < stus.length; i++) {
            if(stus[i].score<60){
                stus[i].score+=2;
            }
        }
    }
    //显示本组学生成绩信息
    public void shouStus(Student [] stus){
        for(Student stu:stus){
            stu.showInfo();
        }
    }
}

public class TestModifyStu {
    public static void main(String[] args) {
        ModifyScore m=new ModifyScore();
        //定义一个学生对象数组
        Student s1=new Student();
        s1.name="张三";
        s1.score=43;
        Student s2=new Student();
        s2.name="李四";
        s2.score=93;
        Student s3=new Student();
        s3.name="王五";
        s3.score=59;
        Student [] stus=new Student[3];
        stus[0]=s1;
        stus[1]=s2;
        stus[2]=s3;
        //显示学生信息
        m.shouStus(stus);
        m.modifyStuScore(stus);
        m.shouStus(stus);

    }
}

类与对象的关系

类:抽象的数据类型,对某一类事物真题描述/定义,不能代表某一具体事物

对象是抽象的具体

类是对象的抽象

创建与初始化对象

  • 使用new关键字创建对象
  • 使用new创建时,除了分配内存空间,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
  • 类中构造器也称为构造方法,是在进行创建对象的时候必须调用的,构造器有以下特点
    • 必须和类名字相同
    • 必须没有返回类型,也不能写void
    • 一个类即使什么都不写,也会存在一个方法

作用:

  • 使用New关键字本质是在调用构造器
  • 初始化对象的值

注意:一旦定义了有参构造,无参构造就必须显示定义

​ 单例模式,需要构造器私有

栈存放引用,堆存放具体的对象

alt+insert 生成构造器

构造方法

例Student s1=new Student(); 无参构造方法:每个类都有默认无参构造方法,,系统默认提供无参构造方法

访问修饰符 构造方法名(){ //初始化代码}

特殊处:无返回值类型(不写void),方法名和类名相同

作用:完成对象初始化

例public Student(String name,int score){

name =name; //第一个是属性,第二个是形参

score =score; //第一个是属性,第二个是形参}

//可以理解为这样

public Student(String n,int s){

name =n; //第一个是属性,第二个是形参

score =s; //第一个是属性,第二个是形参}

一般不这样写

一般这样写:this 指代当前对象

public Student(String name,int score){

this.name =name; //第一个是属性,第二个是形参

this.score =score; //第一个是属性,第二个是形参}

Student s1=new Student();
s1.name=“张三”;
s1.score=43;

换成

Student s1=new Student(“张三”,43);

this 关键字作用

  1. 调用属性

    public Student(String name,int score){

    this.name =name; //第一个是属性,第二个是形参

    this.score =score; //第一个是属性,第二个是形参}

  2. 调用方法

    public void showInfo(){

    System.out.println(name+“的成绩是:”+score);}

    public void method1(){

    showInfo();

    //等同于this.showInfo();}

  3. 调用构造方法

    public Student(String name,int score){

    this.name =name; //第一个是属性,第二个是形参

    this.score =score; //第一个是属性,第二个是形参}

​ public Student(String name,int score,int age){

​ this.name =name; //第一个是属性,第二个是形参

​ this.score =score; //第一个是属性,第二个是形参

​ this.age=age;

//等同于this(name,score);先去调用构造方法public Student(String name,int score),之后单独给age赋值 this.age=age;

等价替换上面3行

this(name,score);

this.age=age;

}

使用this调用构造方法,如果使用,必须是构造方法的第一条语句

带参构造方法,创建学员数组对象的代码

Student s1=new Student();
        s1.name="张三";
        s1.score=43;
        Student s2=new Student();
        s2.name="李四";
        s2.score=93;
        Student s3=new Student();
        s3.name="王五";
        s3.score=59;

改成
Student s1=new Student("张三",43);
Student s2=new Student("李四",93);
Student s3=new Student("王五",59);

如果自己定义了有参构造方法,系统不再默认提供无参构造方法,需要自己定义无参构造方法

构造方法

  1. 作用:对象初始化
  2. 每个类都有默认无参构造方法
  3. 可以自定义带参构造方法,此时系统不再提供默认无参构造方法

this

  1. 对一个对象的默认引用

类与对象小结

类是模版,抽象是对象的一个具体实例

方法:定义调用

对应的引用:基本类型;对象是通过引用来操作的

属性:字段Field成员变量

  • 默认初始化

    数字:0,0.0

    char:u000

    boolean:false

    引用:null

    修饰符 属性类型 属性名 =属性值

对象的创建和使用

  • 必须用new关键字创造对象,构造器Person kuangshen=new Person()
  • 对象的属性:kuangshen.name
  • 对象的方法;kuangshen.sleep()

类:

  • 静态属性
  • 动态行为

java三大特征:封装,继承,多态

封装

程序追求:高内聚,低耦合

高内聚:类的内部数据操作细节自己完成,不允许外部干涉

低耦合:仅暴露少量的方法给外部使用

封装(数据隐藏):通常,应禁止直接访问一个对象中数据的实际表示,而通过操作接口来访问,这称为信息隐藏

两大原则:

  1. 把所有属性藏起来
  2. 把尽可能多的东西藏起来,对外提供便捷的接口

属性私有:private,使用get获取值,set设置值(安全性判断)

实现封装的步骤

  1. 修改属性可见性,改成private,防止错误的修改
  2. 创建公有的getter/setter方法,用于属性的读写
  3. 在getter/setter方法中加入属性控制语句,对属性值的合法性进行判断

原本例子

public class Dog {
    String name="无名氏";//昵称,默认是无名氏
    int health=100;//健康值,默认是100,健康值在0-100之间,小于60 不健康
    int love=0;//亲密度
    String strain="聪明的拉布拉多犬";//品种

    //输出狗狗信息
    public void print(){
        System.out.println("宠物的自白:\n我的名字叫"+this.name+"健康值是:"+this.health
        +",和主人的亲密度是:"+this.love+"我是一只"+this.strain+"。");
    }

    public class Penguin {
    String name="Q仔";//昵称,默认是Q仔
    int health=100;//健康值,默认是100,健康值在0-100之间,小于60 不健康
    int love=0;//亲密度
    String sex="男";//品种

    //输出狗狗信息
    public void print(){
        System.out.println("宠物的自白:\n我的名字叫"+this.name+"健康值是:"+this.health
                +",和主人的亲密度是:"+this.love+"我的性别是"+this.sex+"。");
    }

}
    
public class TsetPet {
    public static void main(String[] args) {
        Dog dog=new Dog();
        dog.health=-1000;
        dog.love=3;
        dog.name="多多";
        dog.strain="吉娃娃";
        dog.print();

        Penguin p=new Penguin();
        p.health=-1000;
        p.love=3;
        p.name="Q仔";
        p.sex="男";
        p.print();
    }
}
//健康值都是-1000,有问题,在类里面输入条件语句报错,修改不好改    

修改之后的代码

public class Dog {
    String name="无名氏";//昵称,默认是无名氏
    //1.隐藏属性
     private int health=100;//健康值,默认是100,健康值在0-100之间,小于60 不健康
    private  int love=0;//亲密度
    private String strain="聪明的拉布拉多犬";//品种

    //2.添加属性的setter和getter方法
    //setter方法:属性赋值,属性操作(正确判断)
    //getter方法:属性取值
    public void setHealth(int health){
        if(health<0||health>100){
            System.out.println("宠物狗狗的健康值只能在0-100之间");
            this.health=60;
            return;
        }
        //获取用户给的health
        this.health=health;
    }
    public int getHealth(){
        return this.health;
    }
    public void setName(String name){
        this.name =name;
    }
    public String getName(){
        return this.name;
    }
    public void setLove(int love){
        this.love =love;
    }
    public int getLove(){
        return this.love;
    }
    public void setStrain(String strain){
        this.strain =strain;
    }
    public String getStrain(){
        return this.strain;
    }

    //输出狗狗信息
    public void print(){
        System.out.println("宠物的自白:\n我的名字叫"+this.name+"健康值是:"+this.health
        +",和主人的亲密度是:"+this.love+"我是一只"+this.strain+"。");
    }
}

public class TsetPet {
    public static void main(String[] args) {
        Dog dog=new Dog();
        //dog.health=-1000;
        dog.setHealth(-1000);
        System.out.println(dog.getHealth());
        //dog.love=3;
        dog.setLove(3);
        System.out.println(dog.getLove());
        dog.setName("多多");
        System.out.println(dog.getName());
        //dog.name="多多";
        //dog.strain="吉娃娃";
        dog.setStrain("吉娃娃");
        System.out.println(dog.getStrain());
        dog.print();;

       /* Penguin p=new Penguin();
        p.health=-1000;
        p.love=3;
        p.name="Q仔";
        p.sex="男";
        p.print();*/
    }
}

封装的好处

  1. 便于使用者争取使用系统,防止错误修改属性
  2. 有助于系统之间的松耦合,提高系统独立性
  3. 提高软件的可用性
  4. 降低了构建大型系统的风险

封装应用练习

public class Dog {
    String name="无名氏";//昵称,默认是无名氏
    //1.隐藏属性
     private int health=100;//健康值,默认是100,健康值在0-100之间,小于60 不健康
    private  int love=0;//亲密度
    private String strain="聪明的拉布拉多犬";//品种

    //2.添加属性的setter和getter方法
    //setter方法:属性赋值,属性操作(正确判断)
    //getter方法:属性取值
    public void setHealth(int health){
        if(health<0||health>100){
            System.out.println("宠物狗狗的健康值只能在0-100之间");
            this.health=60;
            return;
        }
        //获取用户给的health
        this.health=health;
    }
    public int getHealth(){
        return this.health;
    }
    public void setName(String name){
        this.name =name;
    }
    public String getName(){
        return this.name;
    }
    public void setLove(int love){
        if(love<0||love>10){
            System.out.println("宠物企鹅的亲密度只能在0-10之间");
        this.love=6;
            return;
    }
    public int getLove(){
        return this.love;
    }
    public void setStrain(String strain){
        this.strain =strain;
    }
    public String getStrain(){
        return this.strain;
    }

    //输出狗狗信息
    public void print(){
        System.out.println("宠物的自白:\n我的名字叫"+this.name+",健康值是:"+this.health
        +",和主人的亲密度是:"+this.love+",我是一只"+this.strain+"。");
    }

}

public class Penguin {
    private String name="Q仔";//昵称,默认是Q仔
    private int health=100;//健康值,默认是100,健康值在0-100之间,小于60 不健康
    private int love=0;//亲密度
    private String sex="男";//品种

    public void setName(String name){
        this.name =name;
    }
    public String getName(){
        return this.name;
    }
    public void setHealth(int health){
        if(health<0||health>100){
            System.out.println("宠物企鹅的健康值只能在0-100之间");
            this.health=60;
            return;
        }
        //获取用户给的health
        this.health=health;
    }
    public int getHealth(){
        return this.health;
    }
    public void setLove(int love){
        if(love<0||love>10){
            System.out.println("宠物企鹅的亲密度只能在0-10之间");
        this.love=6;
            return;
    }
    public int getLove(){
        return this.love;
    }
    public void setSex(String sex){
        this.sex =sex;
    }
    public String getSex(){
        return this.sex;
    }
    //输出企鹅信息
    public void print(){
        System.out.println("宠物的自白:\n我的名字叫"+this.name+"健康值是:"+this.health
                +",和主人的亲密度是:"+this.love+"我的性别是"+this.sex+"。");
    }

}
public class TsetPet {
    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        System.out.println("欢迎您来到宠物店");
        System.out.print("请输入你要领养的宠物的名字");
        String name=input.next();
        System.out.println("请输入要领养宠物的类型:1.狗狗,2.企鹅");
        int typeNo=input.nextInt();
        switch (typeNo){
            case 1:
                System.out.println("请选择狗狗的品种:1.拉布拉多,2.边牧");
                int sexId1=input.nextInt();
                String strain=(sexId1==1)?"拉布拉多":"边牧";
                System.out.println("请输入狗狗的健康值");
                int health1=input.nextInt();
                System.out.println("请输入狗狗的亲密度");
                int love1=input.nextInt();
                Dog dog=new Dog();
                dog.setHealth(health1);
                dog.setLove(love1);
                dog.setName(name);
                dog.setStrain(strain);
                dog.print();
                break;
            case 2:
                System.out.println("请选择企鹅的性别:1.Q妹,2.Q仔");
                int sexId=input.nextInt();
                String sex=(sexId==1)?"Q妹":"Q仔";
                System.out.println("请输入企鹅的健康值");
                int health=input.nextInt();
                System.out.println("请输入企鹅的亲密度");
                int love=input.nextInt();
                Penguin p=new Penguin();
                p.setHealth(health);
                p.setLove(love);
                p.setName(name);
                p.setSex(sex);
                p.print();
                break;
            default:
                System.out.println("暂时没有这个类型的宠物!");
                break;
        }
    }
}

访问权限控制

类的访问修饰符:

  1. public修饰符:公有访问级别
  2. 默认修饰符:包级私有访问级别
//通过public来修饰
public class Student {
}
//默认修饰符
class Person {
}
public class Test {
    public static void main(String[] args) {
        //测试类既可以访问public修饰的类,也可以访问默认修饰符修饰的类
        Student s=new Student();
        Person p=new Person();
    }
}
import cn.com.oop1.Student;
//import cn.com.oop1.Person;
public class Test2 {
    public static void main(String[] args) {
        //不同包的类只可以访问public修饰的类,不可以访问默认修饰符修饰的类
        Student s=new Student();
        //Person p=new Person();
    }
}

类成员的访问控制符

同一个类中同一个包中子类中任何地方
private可以不可以不可以不可以
默认修饰符可以可以不可以(不同包不行,同包可以)不可以
protected可以可以可以不可以
public可以可以可以可以
//通过public来修饰
public class Student {
    //private修饰的类成员,只能在本类中访问
    private int age;
    //默认修饰符,本类能访问
    String name;
    //protected修饰的类成员,本类可以访问
    protected String address;
    //public修饰的类成员,本类可以访问
    public char sex;
    public  void m1(){
        System.out.println(this.age);
        System.out.println(this.name);
        System.out.println(this.address);
        System.out.println(this.sex);
    }
}
import cn.com.oop1.Student;
//import cn.com.oop1.Person;
public class Test2 {
    public static void main(String[] args) {
        //不同包的类只可以访问public修饰的类,不可以访问默认修饰符修饰的类
        Student s=new Student();
        //默认修饰符修饰的类成员,不同包的类不能访问
        System.out.println(s.name);
        //protected修饰的类成员,不同包的其他类可以访问
        System.out.println(s.address);
        //public修饰的类成员,不同包的其他类可以访问
        System.out.println(s.sex);

        //Person p=new Person();
    }
}
public class Test {
    public static void main(String[] args) {
        //测试类既可以访问public修饰的类,也可以访问默认修饰符修饰的类
        Student s=new Student();
        //默认修饰符,本类能访问,同包的其他类也可以访问
        System.out.println(s.name);
        //protected修饰的类成员,同包的其他类可以访问
        System.out.println(s.name);
        //public修饰的类成员,同包的其他类可以访问
        System.out.println(s.sex);
        //其他类不能调用Student中private修饰的类成员
        //s.age;
        s.m1();
        //把m1的修饰符换成private
        Person p=new Person();
    }
}

static变量

static可以用来修饰

  1. 成员变量

    静态变量,可以直接通过类名访问(不用经过对象)

  2. 成员方法

    静态方法,可以直接通过类名访问

  3. 代码块

​ 静态代码块,当Java虚拟机加载类时,就会执行该代码块

类的成员变量包括

  1. 类变量(静态变量)
    • 被static修饰的变量
    • 类内部,可以在任何方法内直接访问
    • 其他类中,可以直接通过类名访问
    • 在内存中只有一个拷贝
    • 能被类的所有实例共享,可作为实例之间进行交流的共享数据
  2. 实例变量
    • 不被static修饰的变量
    • 通过对象(实例)访问
    • 每创建一个对象就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响
public class StaticTest{
    static int num;
     public static void main(String[] args) {
        num++;//1
        StaticTest st1=new StaticTest();
        st1.num++;//2
        StaticTest st2=new StaticTest();
        st2.num++;//3
        StaticTest.num++;//4
        System.out.println(num);
        
    }  
}
//输出结果4

static变量的作用

  1. 能被类的所有实例共享,可作为势力之间进行交流的共享数据
  2. 如果类的所有实例都包含一个相同的常量属性,可把这个属性定义为静态常量类型,从而节省内存空间

例:static final PI=3.14;

练习

模拟实现选民投票过程:一群选民进行投票,每个选民只允许头一次票,并且当投票总数达到100时,就停止投票

//选民类
public class Voter {
    //目前投票数,被所有的选民实例类所共享
    static int count;
    //投票总数最大值,到100时,停止投票
    static final int MAX_COUNT=100;
    private String name;
    public Voter(){}
    public Voter(String name){
        this.name=name;
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name=name;
    }
    //选民投票
    public void vote(){
        if(count==MAX_COUNT){
            System.out.println("投票总数达到100,无法继续投票!");
            return;
        }else {
            count++;
            System.out.println(this.name+"投票成功!当前票数为:"+count);

        }
    }
}

public class TestVoter {
    public static void main(String[] args) {
        Voter v1=new Voter("张三");
        v1.vote();
        Voter v2=new Voter("李四");
        v2.vote();
        Voter v3=new Voter("王五");
        v3.vote();
        for (int i = 0; i < 97; i++) {
            Voter v=new Voter("v"+i);
            v.vote();
        }
        Voter v4=new Voter("张三");
        v4.vote();
    }
}

static方法

  1. 掌握static方法和实例方法的区别
  2. 会定义static方法

static可以用来修饰

  1. 成员变量

    静态变量,可以直接通过类名访问(不用经过对象)

  2. 成员方

    静态方法,可以直接通过类名访问

  3. 代码块

​ 静态代码块,当Java虚拟机加载类时,就会执行该代码块

静态方法:可直接通过类名访问

  1. 静态方法中不能使用this和super
  2. 不能直接访问所属类的实例变量和实例方法
  3. 可直接访问类的静态变量和静态方法

实例方法:通过实例访问

  1. 可直接访问所属类的实例变量,实例方法,静态变量,静态方法

静态方法必须被实现:main是最常用的静态方法

public class Person {
    //属性,成员变量(全局变量)--->实例变量
    int age;
    String name;
    //静态变量 通过类名去调用-->静态变量
    static String email;
    public void m2() {
        System.out.println("实例方法m2");
    }
    //成员方法:实例方法->可以调用实例变量,实例方法,静态变量,静态方法
    public void m1(){
        System.out.println(this.age);
        System.out.println(this.name);
        System.out.println(this.email);
        m2();
        m3();
    }
    //静态方法
    public static void m3(){
        //System.out.println(age);静态方法中不能调用实例变量
        //m1();在静态方法中不能调用实例方法
        //System.out.println(this.age);报错,不能使用this
        System.out.println("静态方法m3");
        System.out.println(email);//在静态方法中可以直接访问静态变量
        m4();//静态方法可以直接调用静态方法
    }
    public static void m4(){
        System.out.println("静态方法m4");
    }
}

public static void main(String[] args) {
        //调用类的成员变量,创建对象,通过对象去访问--->实例变量
        Person p=new Person();
        p.age=18;
        p.name="张三";
        p.email="zhangsan@163.com";
        Person.email="zhangsan@163.com";
        //通过实例调用实例方法
        p.m1();
        //通过类名调用静态方法
        Person.m3();
        Math.random();//静态方法,通过类名直接调用
    }

投票修改输出投票数

//选民类
public class Voter {
    //目前投票数,被所有的选民实例类所共享
    static int count;
    //投票总数最大值,到100时,停止投票
    static final int MAX_COUNT=100;

    private String name;
    public Voter(){}
    public Voter(String name){
        this.name=name;
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name=name;
    }
    //选民投票
    public void vote(){
        if(count==MAX_COUNT){
            System.out.println("投票总数达到100,无法继续投票!");
            return;
        }else {
            count++;
            System.out.println(this.name+"投票成功!当前票数为:"+count);
        }
    }
    //显示当前投票总数
    public static void showCount(){
        System.out.println("当前的投票总数为"+count);
    }
}

public class TestVoter {
    public static void main(String[] args) {
        Voter v1=new Voter("张三");
        v1.vote();
        Voter v2=new Voter("李四");
        v2.vote();
        Voter v3=new Voter("王五");
        v3.vote();
        System.out.println("============");
        Voter.showCount();
        for (int i = 0; i < 97; i++) {
            Voter v=new Voter("v"+i);
            v.vote();
        }
        System.out.println("============");
        Voter.showCount();
        Voter v4=new Voter("张三");
        v4.vote();
        System.out.println("============");
        Voter.showCount();
    }
}

找错

class Dog{
    prviate String name="旺财";
    ......
    public void play(int n){
        static int localv=5;//实例方法中可以访问静态变量,不能定义静态变量
        health=health-n;
        System.out.println(name+" "+localv+" "+health+" "+love);
    }
    public static void main(String [] args){
        Dog d=new Dog();
        d.play(5);
    }
}

static代码块

static可以用来修饰

  1. 成员变量

    静态变量,可以直接通过类名访问(不用经过对象)

  2. 成员方

    静态方法,可以直接通过类名访问

  3. 代码块

​ 静态代码块,当Java虚拟机加载类时,就会执行该代码块

如果有多个静态代码块,按顺序加载

每个静态代码块只会执行一次

public class staticTest {
    static int num=100;
    static {
        num+=100;
        System.out.println(num);
    }
    static {
        num+=100;
        System.out.println(num);
    }
    public static void main(String[] args) {
        staticTest st1=new staticTest();
        staticTest st2=new staticTest();
        System.out.println(staticTest.num);
    }
}
//结果200  300  300

继承

继承本质:对某一批类的抽象,从而实现对现实世界更好的建模

extends:扩展,子类是父类的扩展

java中只有单继承,没有多继承

继承是类与类之间的关系。除此之外,类和类之间的关系还有依赖,组合,聚合等

继承关系的两个类,一个是子类(派生类),一个是父类(基类)。子类继承父类使用关键字extends来表示

子类和父类之间,从意义上讲应该具有is a的关系

使用继承

  1. 编写父类

    访问修饰符 class Pet{//公共的属性和方法}

  2. 编写子类,继承父类

    访问修饰符 class 子类名 extends 父类名{//子类特有的属性和方法}

继承是Java中实现代码重用的重要手段之一。Java中只支持单根继承

一个子类只能有一个父类,一个父类可以有多个子类

public class Dog extends Pet{
    private String strain="聪明的拉布拉多犬";//品种
    public void setStrain(String strain){
        this.strain =strain;
    }
    public String getStrain(){
        return this.strain;
    }}
public class Penguin extends Pet{
    private String sex="男";//品种
    public void setSex(String sex){
        this.sex =sex;
    }
    public String getSex(){
        return this.sex;
    }}
public class Pet {
    String name="无名氏";//昵称,默认是无名氏
    //1.隐藏属性
    private int health=100;//健康值,默认是100,健康值在0-100之间,小于60 不健康
    private  int love=0;//亲密度

    public void setHealth(int health){
        if(health<0||health>100){
            System.out.println("宠物的健康值只能在0-100之间");
            this.health=60;
            return;
        }
        //获取用户给的health
        this.health=health;
    }
    public int getHealth(){
        return this.health;
    }

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

    public void setLove(int love) {
        if (love < 0 || love > 10) {
            System.out.println("宠物的亲密度只能在0-10之间");
            this.love = 6;
            return;
        }
        this.love=love;
    }
    public int getLove(){
        return this.love;
    }
    public void print(){
        System.out.println("宠物的自白:\n我的名字叫"+this.name+",健康值是:"+this.health
                +",和主人的亲密度是:"+this.love+"。");
    }
}

public class TsetPet {
    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        System.out.println("欢迎您来到宠物店");
        System.out.print("请输入你要领养的宠物的名字");
        String name=input.next();
        System.out.println("请输入要领养宠物的类型:1.狗狗,2.企鹅");
        int typeNo=input.nextInt();
        switch (typeNo){
            case 1:
                System.out.println("请选择狗狗的品种:1.拉布拉多,2.边牧");
                int sexId1=input.nextInt();
                String strain=(sexId1==1)?"拉布拉多":"边牧";
                System.out.println("请输入狗狗的健康值");
                int health1=input.nextInt();
                System.out.println("请输入狗狗的亲密度");
                int love1=input.nextInt();
                Dog dog=new Dog();
                dog.setHealth(health1);
                dog.setLove(love1);
                dog.setName(name);
                dog.setStrain(strain);
                dog.print();
                break;

            case 2:
                System.out.println("请选择企鹅的性别:1.Q妹,2.Q仔");
                int sexId=input.nextInt();
                String sex=(sexId==1)?"Q妹":"Q仔";
                System.out.println("请输入企鹅的健康值");
                int health=input.nextInt();
                System.out.println("请输入企鹅的亲密度");
                int love=input.nextInt();
                Penguin p=new Penguin();
                p.setHealth(health);
                p.setLove(love);
                p.setName(name);
                p.setSex(sex);
                p.print();
                break;
            default:
                System.out.println("暂时没有这个类型的宠物!");
                break;
        }
    }
}

子类能继承父类的什么

  1. 继承public和protected修饰的属性和方法,不管是否在同一个包中
  2. 继承默认权限修饰符的属性和方法,必须在同一个包中
package cn.com.oop1;
public class Person {
    private int age;
    protected String name;
    char sex;
    public String address;
}

package cn.com.oop1;
//通过public来修饰
public class Student extends Person{
    public void m1(){
        //子类可以继承父类的protected成员,不管父子类是否在同一个包
        System.out.println(name);
        //报错,父类私有。子类无法继承父类的private成员
        //System.out.println(age);
        System.out.println(sex);
        System.out.println(address);
    }
}

package cn.com.oop2;
public class Teacher extends Person{
    public void m1(){
        System.out.println(name);
        //报错,父类私有。子类无法继承父类的private成员
        //System.out.println(age);
        //同包子类可以继承父类的默认修饰符修饰的成员,不同包不能继承
        //System.out.println(sex);
        System.out.println(address);
    }
}

子类不能继承父类的什么

  1. private成员
  2. 子类和父类不在同一个包里,默认修饰符不能继承
  3. 构造方法,但是可以调用

object类(祖宗)

super(父)——this(当前的)

  1. super调用父类的构造方法,必须在构造方法的第一个
  2. super必须只能出现在子类的方法或者构造方法中
  3. super和this不能同时调用构造方法

Vs this

  1. 代表的对象不同

​ this:本身调用者这个对象

​ super:代表父类对象的应用

  1. 前提

    ​ this:没有继承也可以使用

    ​ super:只能在继承条件下使用

  2. 构造方法

    ​ this:本类的构造

    ​ super:父类的构造

想要宠物实现自己的独有属性?

  1. 子类分别自己进行输出
  2. 在子类分别定义方法,输出自己独有的信息
  3. 子类重写父类方法

方法重写或覆盖

  1. 子类根据需求从父类继承的方法进行重写
  2. 重写时,可以用super.方法的方式来保留父类的方法
  3. 构造方法不能被重写
  4. 有继承关系的类之间,同名方法
public class Dog extends Pet{
    private String strain="聪明的拉布拉多犬";//品种
    public void setStrain(String strain){
        this.strain =strain;
    }
    public String getStrain(){
        return this.strain;
    }
    public void print(){
        //调用父类的print()
        super.print();
        System.out.println("我是一只:"+this.strain);
    }
}

public class Penguin extends Pet{
    private String sex="男";//品种
    public void setSex(String sex){
        this.sex =sex;
    }
    public String getSex(){
        return this.sex;
    }
    public void print(){
        //调用父类的print()
        super.print();
        System.out.println("我是一只:"+this.sex);
    }
}

//Student是子类,Person是父类
public class Student extends Person{
    public Student(){
        System.out.println("Student无参执行了")
    }
    private String name="qinjiang";
     public void print(){
        System.out.println("Student");
    }
    public void test(String name){
        System.out.println(name);//秦疆
        System.out.println(this.name);//qinjaing
        System.out.println(super.name);//kuangshen
    }    
    public void test2(){
        print();
        this.print();//自己的print
        super.print();//父类的print
}
//父类
public class Person/*extends Object */{
    public Person(){
        System.out.println("Person无参执行了")
    }
    public String name="kuangshen";
    //public改成private,后面的super.print就会出问题
    //显示私有的无法被继承
    public void print(){
        System.out.println("Person");
    }
}

public class Application{
    public static void main(String[] args){
        Student student=new Student();
        student.test=("秦疆");
    }
}
//输出秦疆 qinjaing kuangshen

public class Application1{
    public static void main(String[] args){
        Student student=new Student();
        student.test2();
    }
}
    //输出 Student Student Person
//Student是子类,Person是父类
public class Student extends Person{
    public Student(){
        //隐藏代码:调用了父类的无参构造super();
        //super();必须放在子类构造器的第一行,否则会报错
        //如果父类是有参构造,子类不能调用无参,必须在父类中先定义一个无参,子类可以直接调用父类的有参
        System.out.println("Student无参执行了")
    }
    private String name="qinjiang";
     public void print(){
        System.out.println("Student");
    }
    public void test(String name){
        System.out.println(name);//秦疆
        System.out.println(this.name);//qinjaing
        System.out.println(super.name);//kuangshen
    }    
    public void test2(){
        print();
        this.print();//自己的print
        super.print();//父类的print
}
//父类
public class Person/*extends Object */{
    public Person(){
        System.out.println("Person无参执行了")
    }
    protected String name="kuangshen";
    //public改成private,后面的super.print就会出问题
    //显示私有的无法被继承
    public void print(){
        System.out.println("Person");
    }
}

public class Application{
    public static void main(String[] args){
        Student student=new Student();
        //student.test=("秦疆");
        //student.test2();
        
    }
}
//输出结果 Person无参执行了   Student无参执行了


方法重写

//子类是A,父类是B
//重写是方法的重写,和属性无关
public class B{
    public static void test(){
        System.out.println("B=>test()")}
}

public class A extends B{
    public static void test(){
        System.out.println("A=>test()")}
}

public class Application{
    public static void main(String[] args){
        A a=new A();
        a.test;
    }
}
//输出结果A=>test()

public class Application{
    public static void main(String[] args){
        //静态方法:方法的调用只和左边有关,定义的数据类型
        A a=new A();
        a.test;//A
        //父类的引用指向了子类
        B b=new A;
        b.test;//B
    }
}
//输出结果 A=>test()  B=>test()   
public class B{
    public void test(){
        System.out.println("B=>test()")}
}

public class A extends B{
    @Override//单词本身是重写的意思。  注解:有功能的注释
    public void test(){
        System.out.println("A=>test()")//变成
        //super.test();
    }
}

public class Application{
    public static void main(String[] args){
        //静态方法和非静态的方法区别很大
        //静态方法:方法的调用只和左边有关,定义的数据类型
        //非静态方法可以选择重写
        A a=new A();
        a.test;
        
        B b=new A;//子类重写了父类的方法
        b.test;
    }
}
//输出结果 A=>test()  A=>test()

重写注:条件:需要有继承关系,子类重写父类的方法

  1. 方法名必须相同
  2. 参数列表必须相同
  3. 修饰符:范围可以扩大不能缩小 public>protected>default>private
  4. 抛出异常:范围可以缩小不能扩大ClassNotFoundException–>Exception(大)
  5. 重写子类和父类必须一致,方法体不同

为什么需要重写:

  1. 父类功能子类并不一定需要或者不一定满足
  2. Alt+Insert; override

super介绍

  1. 了解super的作用
  2. 借助super能实现子类访问父类成员

子类访问父类成员

  1. 子类访问父类方法(必须是非private方法)

    super.print();

  2. 访问父类属性;不可以调用private属性,可以调用非private属性

    super.name;

  3. 访问父类构造方法

    super();调用父类无参构造方法

    super(name);调用父类有参构造方法

super关键字来访问父类的成员

  1. 使用super关键字,super代表父类对象

  2. super只能出现在子类的方法和构造方法中

  3. super调用构造方法时,super只能是第一句

  4. super不能访问父类的private成员

    区别thissuper
    访问属性访问本类属性,如果没有就从父类找访问父类的属性
    访问方法访问本类方法,如果没有就从父类找访问父类中的方法
    访问构造访问本类构造,放在构造方法首行调用父类构造,放在子类构造方法首行

访问父类的父类的成员,用什么方式?

也是通过super就可以

java支持单根继承,可以有多个间接父类

//父类:人类
public class Person extends Animal{
    private String name;
    public String getName(){
        return name;
    }
    public void setSex(String name){
        this.name=name;
    }
}

//孙子类
public class Student extends Person{
    private String sid;
    public String getSid(){
        return sid;
    }
    public void setSid(String sid){
        this.sid=sid;
    }
    public void print(){
        super.print();
    }

    public static void main(String[] args) {
        Student s=new Student();
        s.print();
    }
}

//爷爷类
public class Animal {
    private int age;
    private String sex;

    public int getAge(){
        return age;
    }
    public void setAge(int age){
        this.age=age;
    }
    public String getSex(){
        return sex;
    }
    public void setSex(String sex){
        this.sex=sex;
    }
    public void print(){
        System.out.println("这是爷爷类的方法");
    }
}

比较项位置方法名参数表返回值访问修饰符
方法重写子类相同相同相同或是其子类不能比父类严格
方法重载同类相同不相同无关无关

多态

多态:同一方法可以根据发送对象的不同采用多种不同的行为方式。

一个对象的实际类型是确定的,但可以指向对象的引用类型有很多,不确定

多态条件:

  1. 有继承关系

    2. 子类重写父类
    2. 父类引用指向子类对象
    
public class Student extends Person{
    
}

public class Person{
    public void run(){
        System.out.println("run");
    }
}
//重写
public class Person{
    @Override
    public void run(){
        System.out.println("son");
    }
    public void eat(){
        System.out.println("ear");
	}
}

public class Application{
    //一个对象的实际类型是确定的
    //new Student();
    //newPerson();
    //可以指向的引用类型就不确定了
    //Student能调用的方法都是自己的或者继承父类的
    Student s1=new Student();
    //Person付类型,可以指向子类,到时不能调用子类独有的方法
    Person s2=new Student();//父类
    String s3=new Student();//报错
    Object=new Student();//object是祖宗类
    s2.run();//子类重写了父类的方法,执行子类的方法
    s1.run();
}

public class Application{
    //一个对象的实际类型是确定的
    //new Student();
    //newPerson();
    //可以指向的引用类型就不确定了
    sStudent s1=new Student();
    Person s2=new Person();//父类
    String s3=new Student();//报错
    Object=new Student();//object是祖宗类
    //能执行哪些方法主要看左边的类型,和右边关系不大
    s2.eat();//报错,改成((Student)s2).eat();强制转换
    s1.eat();
}

注意

  1. 多态是方法的多态,属性没有多态

  2. 父类和子类有联系,(类型转换异常)ClassCastException!(类型转换异常)

  3. 存在条件:继承关系,方法需要重写,父类引用指向子对象! father f1=new son();

    不能重写,不能实现多态

    1. static方法,属于类,不属于实例
    2. final常量
    3. private

instanceof和类型转换

instanceof:判断两个类之间是否存在父子关系,引用类型(判断一个对象是什么类型)

public class Application{
    public static void main(String[] args) {
        //object>person>teacher
        //object>person>student
        //object>string
        Object object=new Student();
        System.out.println(object instanceof Student);//true
         System.out.println(object instanceof Person);//true
         System.out.println(object instanceof Object);//true
         System.out.println(object instanceof Teacher);//false
         System.out.println(object instanceof String);//false
        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);//报错
        Student student=new Student();
        System.out.println(student instanceof Student);//true
         System.out.println(student instanceof Person);//true
         System.out.println(student instanceof Object);//true
        System.out.println(student instanceof Teacher);//报错
         System.out.println(student instanceof String);//报错
    }
}
public class Person{   
    public void run(){
        System.out.println("run");
    }
}
public class Student extends Person{ 
    public void go(){
        System.out.println("go");
}
public class Teacher extends Person{    
}

System.out.println(X instanceof Y);//能不能通过编译

类型之间的转换

//类之间的转化:父 子

//高 低

public class Person{   
    public void run(){
        System.out.println("run");
    }
}
public class Student extends Person{ 
    public void go(){
        System.out.println("go");}
}
    
public class Application{
    public static void main(String[] args) {
        Person obj=new Student();
        student.go();//报错,需要转换
        //将student对象转换成Student,就可以使用了
        (Student)student=(Student) obj;
        student.go();//上面两行换成((Student)obj).go();
        
    }
}
public class Person{   
    public void run(){
        System.out.println("run");
    }
}
public class Student extends Person{ 
    public void go(){
        System.out.println("go");
	}
}

public class Application{
    public static void main(String[] args) {
        //子类转换成父类,可能会丢失一些方法
        Student student=new Student();
        student.go();
        Person person=student;
        person.go();//自动换成((Student)person).go();  
    }
}

条件

  1. 父类引用指向子类对象
  2. 把子类转换成父类,向上转型
  3. 把父类转换成子类,向下转型;强制转换
  4. 方便方法的调用,减少重复的代码,简洁

static总结

public class Student{
    private static int age;//静态变量  多线程
    private double score;//非静态变量
     public static void main(String[] args) {
     	Student s1=new Student();
         System.out.println(Student.age);
         System.out.println(Student.score);//报错,
         System.out.println(s1.age);
         System.out.println(s1.score);
     }
}
public class Student{
    private static int age;//静态变量  多线程
    private double score;//非静态变量
    public void run(){
        
    }
    public static void go(){
        
    }
    public static void main(String[] args) {
    	Student.go();
        //go();也可以
        run();//报错
    }
public class Person{
    //2
    {
        System.out.println("匿名代码块");
    }
    //1
    static{
        System.out.println("静态代码块");
    }
    //3
    public Person(){
        System.out.println("构造方法");
    }
    public static void main(String[] args){
        Person person=new Person();
    }
}
//输出 静态代码块  匿名代码块  构造方法 
public class Person{
    //2,可以用这种方法赋初始值
    {
        System.out.println("匿名代码块");
    }
    //1
    static{
        //只执行一次
        System.out.println("静态代码块");
    }
    //3
    public Person(){
        System.out.println("构造方法");
    }
    public static void main(String[] args){
        Person person=new Person();
        System.out.println("========");
        Person person1=new Person();
    }
}
//输出  静态代码块   匿名代码块  构造方法 ========匿名代码块  构造方法
public class Test{
    public static void main(String[] args){
        System.out.println(Math.random());
    }
}
//修改
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test{
    public static void main(String[] args){
        System.out.println(random());
        System.out.println(PI);
    }
}

被final修饰的类不能被继承

抽象类

abstract修饰符可以用来修饰方法,也可以用来修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类

抽象类中可以没有抽象方法,但是抽象方法的类一定要声明为抽象类

抽象方法,只有方法的声明,没有方法的实现,他是用来让子类实现的

子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类

抽象类特点:

  1. 抽象类不能用new关键字来创建对象,是用来让子类继承的;约束

  2. 抽象类可以写普通的方法

  3. 抽象方法必须在抽象类中

//抽象类的所有方法,继承了它的子类,都必须实现它的方法
//除非子类也是抽象类
public abstract class A extends stat_ic{
    @Override
    public void doSomething() {

    }
}

//abstract抽象类:类 extends 单继承  多继承
//接口可以多继承
public  abstract class stat_ic {
    //约束,有人帮我们实现
    //abstract 抽象方法,只有方法名字,没有方法的实现
    public abstract void doSomething(){

    }

}

思考

抽象类存在构造器吗

抽象类存在的意义:提供开发效率,扩展性强

异常

异常:软件程序运行过程中,非常可能遇到的异常问题,英文Exception

异常指程序运行中出现的不期而至的各种情况:文件找不到,网络连接失败,非法参数等

异常发生在程序运行期间,他影响了正常的程序执行流程

异常分类:

  1. 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,在编译时不能被忽略
  2. 运行时异常:可能被程序员避免的异常,可以在编译时被忽略
  3. 错误ERROR:不是异常,而是脱离程序员控制的问题。在代码中通常被忽略,编译时也可能检查不出来

​ 异常结构图

ERROR:ERROR类对象由Java虚拟机生成并抛出

Exception异常

  1. 数组下标越界ArraylndexOutOfBoundsException
  2. 运行时异常 RuntimeException
  3. 空指针异常 NullPointerException
  4. 算术异常 ArithmeticException
  5. 丢失资源 MissingResourcesException
  6. 找不到类 ClassNotFoundException

ERROR异常

  1. 栈溢出StackoVERfLOW
  2. 内存溢出OutOfMenory

两者区别:

ERROR通常是灾难性的致命错误,是城西湖无法处理的控制的

Exception通常可以被程序处理的,应该尽可能处理这些异常

异常处理机制

抛出异常

捕获异常

异常处理的五个关键字:try {},catch() ,finally{}, throw内部手动抛出异常 ,throws方法抛出

public static void main(String[] args) {
        int a=1;
        int b=0;
        try {
            //try可以监控区域
            System.out.println(a/b);
        } catch (Exception e) {//catch捕获异常
            System.out.println("程序出现异常,变量b不能为0");
        }finally {//处理善后工作
            System.out.println("finally");
        }
        //try和catch必须存在,可以不要finally
        //IO流,finally可以用来进行资源关闭
    }
//输出程序出现异常,变量b不能为0
//finally

public static void main(String[] args) {
        int a=1;
        int b=0;
        try {
            //try可以监控区域
            new exception().a();
        } catch (Exception e) {//catch捕获异常
            System.out.println("程序出现异常,变量b不能为0");
        }finally {//处理善后工作
            System.out.println("finally");
        }
        //try和catch必须存在,可以不要finally
        //IO流,finally可以用来进行资源关闭
    }
    public void a(){
        b();
    }
    public void b(){
        a();
    }
}
//输出结果finally+报错
public static void main(String[] args) {
        int a=1;
        int b=0;
        try {
            //try可以监控区域
            new exception().a();
        } catch (Throwable e) {//catch捕获异常
            System.out.println("程序出现异常,变量b不能为0");
        }finally {//处理善后工作
            System.out.println("finally");
        }
        //try和catch必须存在,可以不要finally
        //IO流,finally可以用来进行资源关闭
    }
    public void a(){
        b();
    }
    public void b(){
        a();
    }
}
//输出结果程序出现异常,变量b不能为0
//finally

catch( ),里面是想要捕获的异常类型。最高Throwable,其次是ERROR和Exception

//捕获多个异常
public static void main(String[] args) {
        int a=1;
        int b=0;
        try {
            //try可以监控区域
            //要捕获多个异常,从小到大
            System.out.println(a/b);
        } catch (Error e) {//catch捕获异常
            System.out.println("Error");
        }
        catch (Exception e) {//catch捕获异常
            System.out.println("Exception");
        }
        catch (Throwable e) {//catch捕获异常
            System.out.println("Throwable");
        } finally {//处理善后工作
            System.out.println("finally");
        }
        //try和catch必须存在,可以不要finally
        //IO流,finally可以用来进行资源关闭
    }
//输出Exception   finally

Ctrl+alt+T快捷键代码块

//程序结束代码
System.exit(0);
//主动抛出异常,一般在方法中使用
public class exception {
    public static void main(String[] args) {
        new exception().test(1,0);
    }
    //假设这个方法中处理不了异常,方法上抛出异常
    public void test(int a,int b){
        if (b == 0) {
            throw new ArithmeticException();

        }
    }
}

//假设这个方法中处理不了异常,方法上抛出异常
public void test(int a,int b) throws ArithmeticException{

        }

自定义异常

Java内置的异常类可以处理大多数异常,此外,用户还可以自定义异常

用户自定义异常,只需要继承Exception类即可

自定义异常步骤

  1. 创建自定义异常类
  2. 通过throw关键字抛出异常对象
  3. 在当前抛出异常方法中处理异常,用try-catch语句捕获,否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作
  4. 在出现异常方法的调用者捕获并处理异常
    static void test(int a) throws ai{
        System.out.println("传递的参数为"+a);
        if(a>10){
            throw new ai(a);
        }
        System.out.println("OK");
    }

    public static void main(String[] args) {
        try {
            test(11);
        } catch (ai e) {
            System.out.println("ai=>"+e);
        }
    }
}
//输出传递的参数为11   ai=>ai{detail=11}

总结

  1. 处理运行时异常,采用逻辑去合理规避同时辅助try -catch处理
  2. 多重catch块后面,可以加一个catch(Exception)来处理可能遗漏的异常
  3. 不确定的,加上try -catch,处理潜在的异常
  4. 尽量去处理异常,切忌只是简单的调用printStackTrace()去打印输出
  5. 根据不同的业务需求和异常类型去处理
  6. 尽量添加finally语句块去释放占用的资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值