JAVA SE复习(第5章 面向对象(上))

news2024/11/25 0:48:40

本文笔记来自硅谷柴林燕老师的笔记 只为自己看笔记方便使用 不做他用

5.1 面向对象编程


5.1.2 类和对象

1、什么是类

是一类具有相同特性的事物的抽象描述,是一组相关属性行为的集合。

  • 属性:就是该事物的状态信息。

  • 行为:就是在你这个程序中,该状态信息要做什么操作,或者基于事物的状态能做什么。

2、什么是对象

对象是一类事物的一个具体个体(对象并不是找个女朋友)。即对象是类的一个实例,必然具备该类事物的属性和行为。

例如:做一个养宠物的小游戏

类:人、猫、狗等

 publicclassDog{
     Stringtype; //种类
     Stringnickname; //昵称
     intenergy; //能量
     finalintMAX_ENERGY=10000;
     
     //吃东西
     voideat(){
         if(energy<MAX_ENERGY){
             energy+=10;
         }       
     }
 }
 publicclassPerson{
     Stringname;
     chargender;
     Dogdog;
     
     //喂宠物
     voidfeed(){
         dog.eat();
     }
 }
 publicclassGame{
     publicstaticvoidmain(String[] args){
         Personp=newPerson();
         p.name="张三";
         p.gender='男';
         p.dog=newDog();
         
         p.dog.type="哈巴狗";
         p.dog.nickname="小白";
         
         for(inti=1; i<=5; i++){
             p.feed();
         }
         
         System.out.println(p.dog.energy);
     }
 }

3、类与对象的关系

  • 类是对一类事物的描述,是抽象的

  • 对象是一类事物的实例,是具体的

  • 类是对象的模板,对象是类的实体

5.1.3 如何定义类

1、类的定义格式

关键字:class(小写)

 【修饰符】class类名{
 ​
 }

类的定义格式举例:

 publicclassStudent{
     
 }

2、对象的创建

关键字:new

 new类名()//也称为匿名对象
 ​
 //给创建的对象命名
 //或者说,把创建的对象用一个引用数据类型的变量保存起来,这样就可以反复使用这个对象了
 类名对象名=new类名();

那么,对象名中存储的是什么呢?答:对象地址

 publicclassTestStudent{
     publicstaticvoidmain(String[] args){
         System.out.println(newStudent());//Student@7852e922
 ​
         Studentstu=newStudent();
         System.out.println(stu);//Student@4e25154f
         
         int[] arr=newint[5];
         System.out.println(arr);//[I@70dea4e
     }
 }

发现学生对象和数组对象类似,直接打印对象名和数组名都是显示“类型@对象的hashCode值",所以说类、数组都是引用数据类型,引用数据类型的变量中存储的是对象的地址,或者说指向堆中对象的首地址。

那么像“Student@4e25154f”是对象的地址吗?不是,因为Java是对程序员隐藏内存地址的,不暴露内存地址信息,所以打印对象时不直接显示内存地址,而是JVM帮你调用了对象的toString方法,将对象的基本信息转换为字符串并返回,默认toString方法返回的是“对象的运行时类型@对象的hashCode值的十六进制值”,程序员可以自己改写toString方法的代码(后面会讲如何改写)。

5.2 包(Package)


5.2.1 包的作用

(1)可以避免类重名:有了包之后,类的全名称就变为:包.类名

(2)可以控制某些类型或成员的可见范围

如果某个类型或者成员的权限修饰缺省的话,那么就仅限于本包使用。

(3)分类组织管理众多的类

例如:

  • java.lang----包含一些Java语言的核心类,如String、Math、Integer、 System和Thread等,提供常用功能

  • java.net----包含执行与网络相关的操作的类和接口。

  • java.io ----包含能提供多种输入/输出功能的类。

  • java.util----包含一些实用工具类,如集合框架类、日期时间、数组工具类Arrays,文本扫描仪Scanner,随机值产生工具Random。

  • java.text----包含了一些java格式化相关的类

  • java.sql和javax.sql----包含了java进行JDBC数据库编程的相关类/接口

  • java.awt和java.swing----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。

5.2.2 如何声明包

关键字:package

package包名;

注意:
(1)必须在源文件的代码首行
(2)一个源文件只能有一个声明包的package语句

包的命名规范和习惯:(1)所有单词都小写,每一个单词之间使用.分割(2)习惯用公司的域名倒置开头

例如:com.atguigu.xxx;

建议大家取包名时不要使用“java.xx"包

5.2.3 如何跨包使用类

==注意:==只有public的类才能被跨包使用

(1)使用类型的全名称

例如:java.util.Scanner input = new java.util.Scanner(System.in);

(2)使用import 语句之后,代码中使用简名称

import语句告诉编译器到哪里去寻找类。

import语句的语法格式:

 import包.类名;
 import包.*;
注意:
使用java.lang包下的类,不需要import语句,就直接可以使用简名称
import语句必须在package下面,class的上面
当使用两个不同包的同名类时,例如:java.util.Date和java.sql.Date。一个使用全名称,一个使用简名称

示例代码:

 packagecom.atguigu.test02.pkg;
 ​
 importcom.atguigu.test01.oop.Student;
 ​
 importjava.util.Date;
 importjava.util.Scanner;
 ​
 publicclassTestPackage {
     publicstaticvoidmain(String[] args) {
 /*        java.util.Scanner input = new java.util.Scanner(System.in);
         com.atguigu.test01.oop.Student stu = new com.atguigu.test01.oop.Student();*/
 ​
         Scannerinput=newScanner(System.in);
         Studentstudent=newStudent();
 ​
         Dated1=newDate();
         java.sql.Dated2=newjava.sql.Date(0);
     }
 }

5.3 成员变量


5.3.1 如何声明成员变量

 【修饰符】class类名{
     【修饰符】数据类型  成员变量名; 
 }

示例:

 publicclassPerson{
     Stringname;
     chargender;
     intage;
 }

位置要求:必须在类中,方法外

类型要求:可以是Java的任意类型,包括基本数据类型、引用数据类型(类、接口、数组等)

修饰符:成员变量的修饰符有很多,例如:public、protected、private、static、volatile、transient、final等,后面会一一学习。

其中static可以将成员变量分为两大类,静态变量和非静态变量。其中静态变量又称为类变量,非静态变量又称为实例变量或者属性。==接下来先学习实例变量。==

5.3.2 对象的实例变量

1、实例变量的特点

(1)实例变量的值是属于某个对象的

  • 必须通过对象才能访问实例变量

  • 每个对象的实例变量的值是独立的

(2)实例变量有默认值

分类

数据类型

默认值

基本类型

整数(byte,short,int,long)

0

浮点数(float,double)

0.0

字符(char)

'\u0000'

布尔(boolean)

false

数据类型

默认值

引用类型

数组,类,接口

null

2、实例变量的访问

 对象.实例变量

例如:

 packagecom.atguigu.test03.field;
 ​
 publicclassTestPerson {
     publicstaticvoidmain(String[] args) {
         Personp1=newPerson();
         p1.name="张三";
         p1.age=23;
         p1.gender='男';
 ​
         Personp2=newPerson();
         /*
         (1)实例变量的值是属于某个对象的
         - 必须通过对象才能访问实例变量
         - 每个对象的实例变量的值是独立的
         (2)实例变量有默认值
          */
         System.out.println("p1对象的实例变量:");
         System.out.println("p1.name = "+p1.name);
         System.out.println("p1.age = "+p1.age);
         System.out.println("p1.gender = "+p1.gender);
 ​
         System.out.println("p2对象的实例变量:");
         System.out.println("p2.name = "+p2.name);
         System.out.println("p2.age = "+p2.age);
         System.out.println("p2.gender = "+p2.gender);
     }
 }

3、实例变量的内存分析

内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。只要计算机在运行中,CPU就会把需要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来。我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存。Java虚拟机要运行程序,必须要对内存进行空间的分配和管理,每一片区域都有特定的处理数据方式和内存管理方式。

JVM的运行时内存区域分为:方法区、堆、虚拟机栈、本地方法栈、程序计数器几大块。

区域名称

作用

程序计数器

程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址

本地方法栈

当程序中调用了native的本地方法时,本地方法执行期间的内存区域

方法区

存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

堆内存

存储对象(包括数组对象),new来创建的,都存储在堆内存。

虚拟机栈

用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型、对象引用,方法执行完,自动释放。

Java对象保存在内存中时,由以下三部分组成:

  • 对象头

  • Mark Word:记录了和当前对象有关的GC、锁等信息。(后面再讲)

  • 指向类的指针:每一个对象需要记录它是由哪个类创建出来的,而Java对象的类数据保存在方法区,指向类的指针就是记录创建该对象的类数据在方法区的首地址。该指针在32位JVM中的长度是32bit,在64位JVM中长度是64bit。

  • 数组长度(只有数组对象才有)

  • 实例数据

  • 即实例变量的值

  • 对齐填充

  • 因为JVM要求Java对象占的内存大小应该是8bit的倍数,如果不满足该大小,则需要补齐至8bit的倍数,没有特别的功能。

5.4 方法(Method)


5.4.1 方法的概念

方法也叫函数,是一组代码语句的封装,从而实现代码重用,从而减少冗余代码,通常它是一个独立功能的定义,方法是一个类中最基本的功能单元。

 Math.random()的random()方法
 Math.sqrt(x)的sqrt(x)方法
 System.out.println(x)的println(x)方法
 ​
 Scannerinput=newScanner(System.in);
 input.nextInt()的nextInt()方法

5.4.2 方法的特点

(1)必须先声明后使用

类,变量,方法等都要先声明后使用

(2)不调用不执行,调用一次执行一次。

5.4.3 如何声明方法

1、声明方法的位置

声明方法的位置==必须在类中方法外==,即不能在一个方法中直接定义另一个方法。

声明位置示例:

 类{
     方法1(){
         
     }
     方法2(){
         
     }
 }

错误示例:

 类{
     方法1(){
         方法2(){  //位置错误
         
         }
     }
 }

2、声明方法的语法格式

 【修饰符】返回值类型方法名(【形参列表】)【throws异常列表】{
         方法体的功能代码
 }

(1)一个完整的方法 = 方法头 + 方法体。

  • 方法头就是 【修饰符】 返回值类型 方法名(【形参列表 】)【throws 异常列表】,也称为方法签名,通常调用方法时只需要关注方法头就可以,从方法头可以看出这个方法的功能和调用格式。

  • 方法体就是方法被调用后要指定的代码,也是完成方法功能的具体实现代码,对于调用者来说,不了解方法体如何实现的,并影响方法的使用。

(2)方法头可能包含5个部分,但是有些部分是可能缺省的

  • 修饰符:可选的。方法的修饰符也有很多,例如:public、protected、private、static、abstract、native、final、synchronized等,后面会一一学习。其中根据是否有static,可以将方法分为静态方法和非静态方法。其中静态方法又称为类方法,非静态方法又称为实例方法。==接下来咱们先学习实例方法==。

返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者

  • 基本数据类型

  • 引用数据类型

  • 无返回值类型:void

方法名:给方法起一个名字,见名知意,能准确代表该方法功能的名字

参数列表:表示完成方法体功能时需要外部提供的数据列表

  • 无论是否有参数,()不能省略

  • 如果有参数,每一个参数都要指定数据类型和参数名,多个参数之间使用逗号分隔,例如:

  • 一个参数: (数据类型 参数名)

  • 二个参数: (数据类型1 参数1, 数据类型2 参数2)

  • 参数的类型可以是基本数据类型、引用数据类型

  • throws 异常列表:可选,在异常章节再讲

(3)方法体:方法体必须有{}括起来,在{}中编写完成方法功能的代码

关于方法体中return语句的说明:

  • return语句的作用是结束方法的执行,并将方法的结果返回去

  • 如果返回值类型不是void,方法体中必须保证一定有 return 返回值; 语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容。

  • 如果返回值类型为void时,方法体中可以没有return语句,如果要用return语句提前结束方法的执行,那么return后面不能跟返回值,直接写return ; 就可以。

  • return语句后面就不能再写其他代码了,否则会报错:Unreachable code

示例:

 packagecom.atguigu.test04.method;
 ​
 /**
 * 方法定义案例演示
 */
 publicclassMethodDefineDemo {
     /**
      * 无参无返回值方法的演示
      */
     voidsayHello(){
         System.out.println("hello");
     }
 ​
     /**
      * 有参无返回值方法的演示
      * @param length int 第一个参数,表示矩形的长
      * @param width int 第二个参数,表示矩形的宽
      * @param sign char 第三个参数,表示填充矩形图形的符号
      */
     voidprintRectangle(intlength, intwidth, charsign){
         for (inti=1; i<=length ; i++) {
             for(intj=1; j<=width; j++){
                 System.out.print(sign);
             }
             System.out.println();
         }
     }
 ​
     /**
      * 无参有返回值方法的演示
      * @return
      */
     intgetIntBetweenOneToHundred(){
         return (int)(Math.random()*100+1);
     }
     
     /**
      * 有参有返回值方法的演示
      * @param a int 第一个参数,要比较大小的整数之一
      * @param b int 第二个参数,要比较大小的整数之二
      * @return int 比较大小的两个整数中较大者的值
      */
     intmax(inta, intb){
         returna>b?a : b;
     }
 }
 ​

5.4.4 如何调用实例方法

1、方法调用语法格式

 对象.非静态方法(【实参列表】)

例如:

 packagecom.atguigu.test04.method;
 ​
 /**
 * 方法调用案例演示
 */
 publicclassMethodInvokeDemo {
     publicstaticvoidmain(String[] args) {
         //创建对象
         MethodDefineDemomd=newMethodDefineDemo();
 ​
         System.out.println("-----------------------方法调用演示-------------------------");
 ​
         //调用MethodDefineDemo类中无参无返回值的方法sayHello
         md.sayHello();
         md.sayHello();
         md.sayHello();
         //调用一次,执行一次,不调用不执行
 ​
         System.out.println("------------------------------------------------");
         //调用MethodDefineDemo类中有参无返回值的方法printRectangle
         md.printRectangle(5,10,'@');
 ​
         System.out.println("------------------------------------------------");
         //调用MethodDefineDemo类中无参有返回值的方法getIntBetweenOneToHundred
         md.getIntBetweenOneToHundred();//语法没问题,就是结果丢失
 ​
         intnum=md.getIntBetweenOneToHundred();
         System.out.println("num = "+num);
 ​
         System.out.println(md.getIntBetweenOneToHundred());
         //上面的代码调用了getIntBetweenOneToHundred三次,这个方法执行了三次
 ​
         System.out.println("------------------------------------------------");
         //调用MethodDefineDemo类中有参有返回值的方法max
         md.max(3,6);//语法没问题,就是结果丢失
         
         intbigger=md.max(5,6);
         System.out.println("bigger = "+bigger);
 ​
         System.out.println("8,3中较大者是:"+md.max(8,9));
     }
 }
 ​

回忆之前的代码:

 //1、创建Scanner的对象
 Scannerinput=newScanner(System.in);//System.in默认代表键盘输入
 ​
 //2、提示输入xx
 System.out.print("请输入一个整数:"); //对象.非静态方法(实参列表)
 ​
 //3、接收输入内容
 intnum=input.nextInt();  //对象.非静态方法()

2、形参和实参

  • 形参(formal parameter):在定义方法时方法名后面括号中声明的变量称为形式参数(简称形参)即形参出现在方法定义时。

  • 实参(actual parameter):调用方法时方法名后面括号中的使用的值/变量/表达式称为实际参数(简称实参)即实参出现在方法调用时。

  • 调用时,实参的个数、类型、顺序顺序要与形参列表一一对应。如果方法没有形参,就不需要也不能传实参。

  • 无论是否有参数,声明方法和调用方法是==()都不能丢失==

3、返回值问题

方法调用表达式是一个特殊的表达式:

  • 如果被调用方法的返回值类型是void,调用时不需要也不能接收和处理(打印或参与计算)返回值结果,即方法调用表达式==只能==直接加;成为一个独立语句。

  • 如果被调用方法有返回值,即返回值类型不是void,

  • 方法调用表达式的结果可以作为赋值表达式的值,

  • 方法调用表达式的结果可以作为计算表达式的一个操作数,

  • 方法调用表达式的结果可以作为另一次方法调用的实参,

  • 方法调用表达式的结果可以不接收和处理,方法调用表达式直接加;成为一个独立的语句,这种情况,返回值丢失。

 packagecom.atguigu.test04.method;
 ​
 publicclassMethodReturnValue {
     publicstaticvoidmain(String[] args) {
         //创建对象
         MethodDefineDemomd=newMethodDefineDemo();
 ​
         //无返回值的都只能单独加;成一个独立语句
         //调用MethodDefineDemo类中无参无返回值的方法sayHello
         md.sayHello();
         //调用MethodDefineDemo类中有参无返回值的方法printRectangle
         md.printRectangle(5,10,'@');
 ​
         //有返回值的
         //(1)方法调用表达式可以作为赋值表达式的值
         intbigger=md.max(7,3);
         System.out.println("bigger = "+bigger);
 ​
         //(2)方法调用表达式可以作为计算表达式的一个操作数
         //随机产生两个[1,100]之间的整数,并求和
         intsum=md.getIntBetweenOneToHundred() +md.getIntBetweenOneToHundred();
         System.out.println("sum = "+sum);
 ​
         //(3)方法调用表达式可以作为另一次方法调用的实参
         intx=4;
         inty=5;
         intz=2;
         intbiggest=md.max(md.max(x,y),z);
         System.out.println("biggest = "+biggest);
 ​
         //(4)方法调用表达式直接加;成为一个独立的语句,这种情况,返回值丢失
         md.getIntBetweenOneToHundred();
     }
 }

5.4.5 实例方法使用当前对象的成员

在实例方法中还可以使用当前对象的其他成员。在Java中当前对象用this表示。

  • this:在实例方法中,表示调用该方法的对象

  • 如果没有歧义,完全可以省略this。

1、使用this.

案例:矩形类

 packagecom.atguigu.test04.method;
 ​
 publicclassRectangle {
     intlength;
     intwidth;
 ​
     intarea() {
         returnthis.length*this.width;
     }
 ​
     intperimeter(){
         return2* (this.length+this.width);
     }
 ​
     voidprint(charsign) {
         for (inti=1; i<=this.width; i++) {
             for (intj=1; j<=this.length; j++) {
                 System.out.print(sign);
             }
             System.out.println();
         }
     }
 ​
     StringgetInfo(){
         return"长:"+this.length+",宽:"+this.width+",面积:"+this.area() +",周长:"+this.perimeter();
     }
 }

测试类

 packagecom.atguigu.test04.method;
 ​
 publicclassTestRectangle {
     publicstaticvoidmain(String[] args) {
         Rectangler1=newRectangle();
         Rectangler2=newRectangle();
 ​
         System.out.println("r1对象:"+r1.getInfo());
         System.out.println("r2对象:"+r2.getInfo());
 ​
         r1.length=10;
         r1.width=2;
         System.out.println("r1对象:"+r1.getInfo());
         System.out.println("r2对象:"+r2.getInfo());
 ​
         r1.print('#');
         System.out.println("---------------------");
         r1.print('&');
 ​
         System.out.println("---------------------");
         r2.print('#');
         System.out.println("---------------------");
         r2.print('%');
     }
 }

2、省略this.

 packagecom.atguigu.test04.method;
 ​
 publicclassRectangle {
     intlength;
     intwidth;
 ​
     intarea() {
         returnlength*width;
     }
 ​
     intperimeter(){
         return2* (length+width);
     }
 ​
     voidprint(charsign) {
         for (inti=1; i<=width; i++) {
             for (intj=1; j<=length; j++) {
                 System.out.print(sign);
             }
             System.out.println();
         }
     }
 ​
     StringgetInfo(){
         return"长:"+length+",宽:"+width+",面积:"+area() +",周长:"+perimeter();
     }
 }

5.4.6 方法调用内存分析

方法不调用不执行,调用一次执行一次,每次调用会在栈中有一个入栈动作,即给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值,当方法执行结束后,会释放该内存,称为出栈,如果方法有返回值,就会把结果返回调用处,如果没有返回值,就直接结束,回到调用处继续执行下一条指令。

栈结构:先进后出,后进先出。

 packagecom.atguigu.test04.method;
 ​
 publicclassMethodMemory {
     publicstaticvoidmain(String[] args) {
         Rectangler1=newRectangle();
         Rectangler2=newRectangle();
         r1.length=10;
         r1.width=2;
         r1.print('#');
         System.out.println("r1对象:"+r1.getInfo());
         System.out.println("r2对象:"+r2.getInfo());
     }
 }

5.4.7 实例变量与局部变量的区别

1、声明位置和方式(1)实例变量:在类中方法外(2)局部变量:在方法体{}中或方法的形参列表、代码块中

2、在内存中存储的位置不同(1)实例变量:堆(2)局部变量:栈

3、生命周期(1)实例变量:和对象的生命周期一样,随着对象的创建而存在,随着对象被GC回收而消亡, 而且每一个对象的实例变量是独立的。(2)局部变量:和方法调用的生命周期一样,每一次方法被调用而在存在,随着方法执行的结束而消亡, 而且每一次方法调用都是独立。

4、作用域(1)实例变量:通过对象就可以使用,本类中“this.,没有歧义还可以省略this.”,其他类中“对象.”(2)局部变量:出了作用域就不能使用

5、修饰符(后面来讲)(1)实例变量:public,protected,private,final,volatile,transient等(2)局部变量:final

6、默认值(1)实例变量:有默认值(2)局部变量:没有,必须手动初始化。其中的形参比较特殊,靠实参给它初始化。

5.5 参数问题


5.5.1 特殊参数之一:可变参数

JDK1.5之后,当定义一个方法时,形参的类型可以确定,但是形参的个数不确定,那么可以考虑使用可变参数。可变参数的格式:

 【修饰符】 返回值类型 方法名(【非可变参数部分的形参列表,】参数类型... 形参名){  }

可变参数的特点和要求:

(1)一个方法最多只能有一个可变参数

(2)如果一个方法包含可变参数,那么可变参数必须是形参列表的最后一个

(3)在声明它的方法中,可变参数当成数组使用

(4)其实这个书写“≈”

 【修饰符】 返回值类型 方法名(【非可变参数部分的形参列表,】参数类型[] 形参名){  }

只是后面这种定义,在调用时必须传递数组,而前者更灵活,既可以传递数组,又可以直接传递数组的元素,这样更灵活了。

1、方法只有可变参数

案例:求n个整数的和

 packagecom.atguigu.test05.param;
 ​
 publicclassNumberTools {
     inttotal(int[] nums){
         inthe=0;
         for (inti=0; i<nums.length; i++) {
             he+=nums[i];
         }
         returnhe;
     }
 ​
     intsum(int... nums){
         inthe=0;
         for (inti=0; i<nums.length; i++) {
             he+=nums[i];
         }
         returnhe;
     }
 }
 packagecom.atguigu.test05.param;
 ​
 publicclassTestVarParam {
     publicstaticvoidmain(String[] args) {
         NumberToolstools=newNumberTools();
 ​
         System.out.println(tools.sum());//0个实参
         System.out.println(tools.sum(5));//1个实参
         System.out.println(tools.sum(5,6,2,4));//4个实参
         System.out.println(tools.sum(newint[]{5,6,2,4}));//传入数组实参
 ​
         System.out.println("------------------------------------");
         System.out.println(tools.total(newint[]{}));//0个元素的数组
         System.out.println(tools.total(newint[]{5}));//1个元素的数组
         System.out.println(tools.total(newint[]{5,6,2,4}));//传入数组实参
     }
 }

2、方法包含非可变参数和可变参数

  • 非可变参数部分必须传入对应类型和个数的实参;

  • 可变参数部分按照可变参数的规则传入0~n个对应类型的实参或传入1个对应类型的数组实参;

案例:

n个字符串进行拼接,每一个字符串之间使用某字符进行分割,如果没有传入字符串,那么返回空字符串""

 packagecom.atguigu.test05.param;
 ​
 publicclassStringTools {
     Stringconcat(charseperator, String... args){
         Stringstr="";
         for (inti=0; i<args.length; i++) {
             if(i==0){
                 str+=args[i];
             }else{
                 str+=seperator+args[i];
             }
         }
         returnstr;
     }
 }
 packagecom.atguigu.test05.param;
 ​
 publicclassStringToolsTest {
     publicstaticvoidmain(String[] args) {
         StringToolstools=newStringTools();
 ​
         System.out.println(tools.concat('-'));
         System.out.println(tools.concat('-',"hello"));
         System.out.println(tools.concat('-',"hello","world"));
         System.out.println(tools.concat('-',"hello","world","java"));
     }
 }

5.5.2 特殊参数之二:命令行参数(了解)

通过命令行给main方法的形参传递的实参称为命令行参数

 publicclassTestCommandParam{
     //形参:String[] args
     publicstaticvoidmain(String[] args){
         System.out.println(args);
         System.out.println(args.length);
         
         for(inti=0; i<args.length; i++){
             System.out.println("第"+ (i+1) +"个参数的值是:"+args[i]);
         }
     }
 }

命令行:

 java TestCommandParam
 java TestCommandParam 1 2 3
 java TestCommandParam hello atguigu

IDEA工具:

(1)配置运行参数

(2)运行程序

5.5.3 方法的参数传递机制

方法的参数传递机制:实参给形参赋值,那么反过来形参会影响实参吗?

  • 方法的形参是基本数据类型时,形参值的改变不会影响实参;

  • 方法的形参是引用数据类型时,形参地址值的改变不会影响实参,但是形参地址值里面的数据的改变会影响实参,例如,修改数组元素的值,或修改对象的属性值。

  • 注意:String、Integer等特殊类型容易错

1、形参是基本数据类型

案例:编写方法,交换两个整型变量的值

 packagecom.atguigu.test05.param;
 ​
 publicclassPrimitiveTypeParam {
     voidswap(inta, intb){//交换两个形参的值
         inttemp=a;
         a=b;
         b=temp;
     }
 ​
     publicstaticvoidmain(String[] args) {
         PrimitiveTypeParamtools=newPrimitiveTypeParam();
         intx=1;
         inty=2;
         System.out.println("交换之前:x = "+x+",y = "+y);//1,2
         tools.swap(x,y);//实参x,y是基本数据类型,给形参的是数据的“副本”,调用完之后,x与y的值不变
         System.out.println("交换之后:x = "+x+",y = "+y);//1,2
     }
 }

2、形参是引用数据类型

 packagecom.atguigu.test05.param;
 ​
 publicclassReferenceTypeParam {
     voidswap(MyDatamy){//形参my是引用数据类型,接收的是对象的地址值,形参my和实参data指向同一个对象
         //里面交换了对象的两个实例变量的值
         inttemp=my.x;
         my.x=my.y;
         my.y=temp;
     }
 ​
     publicstaticvoidmain(String[] args) {
         ReferenceTypeParamtools=newReferenceTypeParam();
         MyDatadata=newMyData();
         data.x=1;
         data.y=2;
         System.out.println("交换之前:x = "+data.x+",y = "+data.y);//1,2
         tools.swap(data);//实参是data,给形参my的是对象的地址值,调用完之后,x与y的值交换
         System.out.println("交换之后:x = "+data.x+",y = "+data.y);//2,1
     }
 ​
 }
 publicclassMyData{
     intx;
     inty;
 }

3、形参是数组

 packagecom.atguigu.test05.param;
 ​
 publicclassArrayTypeParam {
     voidsort(int[] arr){//给数组排序,修改了数组元素的顺序,这里对arr数组进行排序,就相当于对nums数组进行排序
         for (inti=1; i<arr.length; i++) {
             for (intj=0; j<arr.length-i; j++) {
                 if(arr[j] >arr[j+1]){
                     inttemp=arr[j];
                     arr[j] =arr[j+1];
                     arr[j+1] =temp;
                 }
             }
         }
     }
 ​
     voiditerate(int[] arr){//输出数组的元素,元素之间使用空格分隔,元素打印完之后换行
                             //这个方法没有修改元素的值
         for (inti=0; i<arr.length; i++) {
             System.out.print(arr[i]+" ");
         }
         System.out.println();
     }
 ​
     publicstaticvoidmain(String[] args) {
         ArrayTypeParamtools=newArrayTypeParam();
 ​
         int[] nums= {4,3,1,6,7};
         System.out.println("排序之前:");
         tools.iterate(nums);//实参nums把数组的首地址给形参arr,这个调用相当于输出nums数组的元素
                             //对数组的元素值没有影响
 ​
         tools.sort(nums);//对nums数组进行排序
 ​
         System.out.println("排序之后:");
         tools.iterate(nums);//输出nums数组的元素
         //上面的代码,从头到尾,堆中只有一个数组,没有产生新数组,无论是排序还是遍历输出都是同一个数组
     }
 }

4、形参指向新对象

 packagecom.atguigu.test05.param;
 ​
 publicclassAssignNewObjectToFormalParam {
     voidswap(MyDatamy){
         my=newMyData(); //这里让my形参指向了新对象,此时堆中有两个MyData对象,和main中的data对象无关
         inttemp=my.x;
         my.x=my.y;
         my.y=temp;
      
     }
 ​
     publicstaticvoidmain(String[] args) {
         //创建这个对象的目的是为了调用swap方法
         AssignNewObjectToFormalParamtools=newAssignNewObjectToFormalParam();
         
         MyDatadata=newMyData();
         data.x=1;
         data.y=2;
         System.out.println("交换之前:x = "+data.x+",y = "+data.y);//1,2
         tools.swap(data);//调用完之后,x与y的值交换?
         System.out.println("交换之后:x = "+data.x+",y = "+data.y);//1,2
     }
 }

5.6 方法的重载


  • 方法重载:指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。

  • 参数列表:数据类型个数不同,数据类型不同(按理来说数据类型顺序不同也可以,但是很少见,也不推荐,逻辑上容易有歧义)。

  • 重载方法调用:JVM通过方法的参数列表,调用匹配的方法。

  • 先找个数、类型最匹配的

  • 再找个数和类型可以兼容的,如果同时多个方法可以兼容将会报错

案例,用重载实现:

(1)定义方法求两个整数的最大值

(2)定义方法求三个整数的最大值

(3)定义方法求两个小数的最大值

(4)定义方法求n个整数最大值

 packagecom.atguigu.test06.overload;
 ​
 publicclassMathTools {
     //求两个整数的最大值
     publicintmax(inta,intb){
         returna>b?a:b;
     }
 ​
     //求两个小数的最大值
     publicdoublemax(doublea, doubleb){
         returna>b?a:b;
     }
 ​
     //求三个整数的最大值
     publicintmax(inta, intb, intc){
         returnmax(max(a,b),c);
     }
 ​
     //求n整数的最大值
     publicintmax(int... nums){
         intmax=nums[0];//如果没有传入整数,或者传入null,这句代码会报异常
         for (inti=1; i<nums.length; i++) {
             if(nums[i] >max){
                 max=nums[i];
             }
         }
         returnmax;
     }
 }

1、找最匹配的

 packagecom.atguigu.test06.overload;
 ​
 publicclassMethodOverloadMosthMatch {
     publicstaticvoidmain(String[] args) {
         MathToolstools=newMathTools();
 ​
         System.out.println(tools.max(5,3));
         System.out.println(tools.max(5,3,8));
         System.out.println(tools.max(5.7,2.5));
     }
 }

2、找唯一可以兼容的

 packagecom.atguigu.test06.overload;
 ​
 publicclassMethodOverloadMostCompatible {
     publicstaticvoidmain(String[] args) {
         MathToolstools=newMathTools();
 ​
         System.out.println(tools.max(5.7,9));
         System.out.println(tools.max(5,6,8,3));
 //        System.out.println(tools.max(5.7,9.2,6.9)); //没有兼容的
     }
 }

3、多个方法可以匹配或兼容

 packagecom.atguigu.test06.overload;
 ​
 publicclassMathTools {
     //求两个整数的最大值
     publicintmax(inta,intb){
         returna>b?a:b;
     }
 ​
     //求两个小数的最大值
     publicdoublemax(doublea, doubleb){
         returna>b?a:b;
     }
 ​
     //求三个整数的最大值
     publicintmax(inta, intb, intc){
         returnmax(max(a,b),c);
     }
 ​
     //求n整数的最大值
     publicintmax(int... nums){
         intmax=nums[0];//如果没有传入整数,或者传入null,这句代码会报异常
         for (inti=1; i<nums.length; i++) {
             if(nums[i] >max){
                 max=nums[i];
             }
         }
         returnmax;
     }
 ​
 /*    //求n整数的最大值
     public int max(int[] nums){  //编译就报错,与(int... nums)无法区分
         int max = nums[0];//如果没有传入整数,或者传入null,这句代码会报异常
         for (int i = 1; i < nums.length; i++) {
             if(nums[i] > max){
                 max = nums[i];
             }
         }
         return max;
     }*/
 ​
 /*    //求n整数的最大值
     public int max(int first, int... nums){  //当前类不报错,但是调用时会引起多个方法同时匹配
         int max = first;
         for (int i = 0; i < nums.length; i++) {
             if(nums[i] > max){
                 max = nums[i];
             }
         }
         return max;
     }*/
 }

5.7 方法的递归调用


递归调用:方法自己调用自己的现象就称为递归。

递归的分类:

  • 递归分为两种,直接递归和间接递归。

  • 直接递归称为方法自身调用自己。

  • 间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。

注意事项

  • 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。

  • 在递归中虽然有限定条件,但是递归深度不能太深,否则效率低下,或者也会发生栈内存溢出。

  • 能够使用循环代替的,尽量使用循环代替递归

案例:计算斐波那契数列(Fibonacci)的第n个值,斐波那契数列满足如下规律,

1,1,2,3,5,8,13,21,....

即从第三个数开始,一个数等于前两个数之和。假设f(n)代表斐波那契数列的第n个值,那么f(n)满足:

f(n) = f(n-2) + f(n-1);

 packagecom.atguigu.test07.recursion;
 ​
 publicclassFibonacciTest {
     publicstaticvoidmain(String[] args) {
         FibonacciTestt=newFibonacciTest();
         //创建FibonacciTest的对象,目的是为了调用f方法
 ​
         for(inti=1; i<=10; i++){
             System.out.println("斐波那契数列第"+i+"个数:"+t.f(i));
         }
 ​
         System.out.println(t.f(20));//6765   
     }
 ​
     //使用递归的写法
     intf(intn){//计算斐波那契数列第n个值是多少
         if(n<1){//负数是返回特殊值1,表示不计算负数情况
             return1;
         }
         if(n==1||n==2){
             return1;
         }
         returnf(n-2) +f(n-1);
     }
 }
 ​

 packagecom.atguigu.exer.recursion;
 ​
 publicclassFibonacciTest {
     publicstaticvoidmain(String[] args) {
         FibonacciTestt=newFibonacciTest();
         //创建FibonacciTest的对象,目的是为了调用f方法
 ​
         for(inti=1; i<=10; i++){
             System.out.println("斐波那契数列第"+i+"个数:"+t.fValue(i));
         }
 ​
         System.out.println(t.fValue(20));//6765
 ​
     }
 ​
     //不用递归
     intfValue(intn){//计算斐波那契数列第n个值是多少
         if(n<1){//负数是返回特殊值1,表示不计算负数情况
             return1;
         }
         if(n==1||n==2){
             return1;
         }
         //从第三个数开始,  等于 前两个整数相加
         intbeforeBefore=1; //相当于n=1时的值
         intbefore=1;//相当于n=2时的值
         intcurrent=beforeBefore+before; //相当于n=3的值
         //再完后
         for(inti=4; i<=n; i++){
             beforeBefore=before;
             before=current;
             current=beforeBefore+before;
             /*
             假设i=4
                 beforeBefore = before; //相当于n=2时的值
                 before = current; //相当于n=3的值
                 current = beforeBefore + before; //相当于n = 4的值
             假设i=5
                 beforeBefore = before; //相当于n=3的值
                 before = current; //相当于n = 4的值
                 current = beforeBefore + before; //相当于n = 5的值
                 ....
              */
         }
         returncurrent;
     }
 ​
 ​
 }
 ​

5.8 对象数组


数组是用来存储一组数据的容器,一组基本数据类型的数据可以用数组装,那么一组对象也可以使用数组来装。

即数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用数据类型是,我们称为对象数组。

注意:对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,所以很容易出现空指针异常NullPointerException。

5.8.1 对象数组的声明和使用

案例:

(1)定义矩形类,包含长、宽属性,area()求面积方法,perimeter()求周长方法,String getInfo()返回圆对象的详细信息的方法

(2)在测试类中创建长度为5的Rectangle[]数组,用来装3个矩形对象,并给3个矩形对象的长分别赋值为10,20,30,宽分别赋值为5,15,25,遍历输出

 packagecom.atguigu.test08.array;
 ​
 publicclassRectangle {
     doublelength;
     doublewidth;
 ​
     doublearea(){//面积
         returnlength*width;
     }
 ​
     doubleperimeter(){//周长
         return2* (length+width);
     }
 ​
     StringgetInfo(){
         return"长:"+length+
                 ",宽:"+width+
                 ",面积:"+area() +
                 ",周长:"+perimeter();
     }
 }

 packagecom.atguigu.test08.array;
 ​
 publicclassObjectArrayTest {
     publicstaticvoidmain(String[] args) {
         //声明并创建一个长度为3的矩形对象数组
         Rectangle[] array=newRectangle[3];
 ​
         //创建3个矩形对象,并为对象的实例变量赋值,
         //3个矩形对象的长分别是10,20,30
         //3个矩形对象的宽分别是5,15,25
         //调用矩形对象的getInfo()返回对象信息后输出
         for (inti=0; i<array.length; i++) {
             //创建矩形对象
             array[i] =newRectangle();
 ​
             //为矩形对象的成员变量赋值
             array[i].length= (i+1) *10;
             array[i].width= (2*i+1) *5;
 ​
             //获取并输出对象对象的信息
             System.out.println(array[i].getInfo());
         }
     }
 }

5.8.2 对象数组的内存图分析

对象数组中数组元素存储的是元素对象的首地址。

5.8.3 二维数组

1.什么是二维数组?

一个一维数组只能存储一组同类型的数据,如果需要同时存储多组同类型的数据,就需要使用二维数组。例如,使用一维数组存储一个小组的学员成绩,使用二维数组可以存储多个小组的学员成绩。

  • 二维数组:本质上就是元素为一维数组的一个数组。

  • 二维数组的标记:[][]

 int[][] arr; //arr是一个二维数组,可以看成元素是int[]一维数组类型的一维数组

==二维数组也可以看成一个二维表,行*列组成的二维表==,只不过这个二维表,每一行的列数还可能不同。但是每一个单元格中的元素的数据类型是一致的,例如:都是int,都是String等。

==二维数组也可以看成一个一维数组,只是此时元素是一维数组对象==。

2.二维数组的声明

二维数组声明的语法格式:

 //推荐
 元素的数据类型[][] 二维数组的名称;
 ​
 //不推荐
 元素的数据类型  二维数组名[][];
 //不推荐
 元素的数据类型[]  二维数组名[];

例如:

 publicclassTest20TwoDimensionalArrayDefine {
     publicstaticvoidmain(String[] args) {
         //存储多组成绩
         int[][] grades;
 ​
         //存储多组姓名
         String[][] names;
     }
 }

面试:

 int[] x, y[];
 //x是一维数组,y是二维数组

3.二维数组的静态初始化

静态初始化就是用静态数据(编译时已知)为数组初始化。

 //以下格式要求声明与静态初始化必须一起完成
 元素的数据类型[][] 二维数组的名称= {
             {元素1,元素2,元素3。。。}, 
             {第二行的值列表},
             ...
             {第n行的值列表}
         };
 ​
 元素的数据类型[][] 二维数组名=new元素的数据类型[][]{
             {元素1,元素2,元素3。。。}, 
             {第二行的值列表},
             ...
             {第n行的值列表}
         };
 ​
 元素的数据类型[][] 二维数组名;
 二维数组名=new元素的数据类型[][]{
             {元素1,元素2,元素3。。。}, 
             {第二行的值列表},
             ...
             {第n行的值列表}
         };
如果是静态初始化,右边new 数据类型[][]中不能写数字,因为行数和列数,由{}的元素个数决定

举例:

     int[][] arr= {{1,2,3},{4,5,6},{7,8,9,10}};//声明与初始化必须在一句完成
 ​
     int[][] arr=newint[][]{{1,2,3},{4,5,6},{7,8,9,10}};
 ​
     int[][] arr;
     arr=newint[][]{{1,2,3},{4,5,6},{7,8,9,10}};
 ​
     arr=newint[3][3]{{1,2,3},{4,5,6},{7,8,9,10}};//错误,静态初始化右边new 数据类型[][]中不能写数字

二维数组静态初始化演示:

 publicclassTest21TwoDimensionalArrayInitialize {
     publicstaticvoidmain(String[] args) {
         //存储多组成绩
         int[][] grades= {
                     {89,75,99,100},
                     {88,96,78,63,100,86},
                     {56,63,58},
                     {99,66,77,88}
                 };
 ​
         //存储多组姓名
         String[][] names= {
             {"张三","李四", "王五", "赵六"},
             {"刘备","关羽","张飞","诸葛亮","赵云","马超"},
             {"曹丕","曹植","曹冲"},
             {"孙权","周瑜","鲁肃","黄盖"}
         };
     }
 }

4.二维数组的使用

因为二维数组是用来存储多组数据的,因此要比一维数组麻烦一些,需要我们搞清楚如下几个概念:

  • 二维数组的长度/行数:二维数组名.length

  • 二维数组的某一行:二维数组名[行下标],此时相当于获取其中一组数据。它本质上是一个一维数组。行下标的范围:[0, 二维数组名.length-1]。此时把二维数组看成一维数组的话,元素是行对象。

  • 某一行的列数:二维数组名[行下标].length,因为二维数组的每一行是一个一维数组。

  • 某一个元素:二维数组名[行下标][列下标],即先确定行/组,再确定列。

 publicclassTest22TwoDimensionalArrayUse {
     publicstaticvoidmain(String[] args){
         //存储3个小组的学员的成绩,分开存储,使用二维数组。
         /*
         int[][] scores1;
         int scores2[][];
         int[] scores3[];*/
 ​
         int[][] scores= {
                 {85,96,85,75},
                 {99,96,74,72,75},
                 {52,42,56,75}
         };
 ​
         System.out.println(scores);//[[I@15db9742
         System.out.println("一共有"+scores.length+"组成绩.");
 ​
         //[[:代表二维数组,I代表元素类型是int
         System.out.println(scores[0]);//[I@6d06d69c
         //[:代表一维数组,I代表元素类型是int
         System.out.println(scores[1]);//[I@7852e922
         System.out.println(scores[2]);//[I@4e25154f
         //System.out.println(scores[3]);//ArrayIndexOutOfBoundsException: 3
 ​
         System.out.println("第1组有"+scores[0].length+"个学员.");
         System.out.println("第2组有"+scores[1].length+"个学员.");
         System.out.println("第3组有"+scores[2].length+"个学员.");
 ​
         System.out.println("第1组的每一个学员成绩如下:");
         //第一行的元素
         System.out.println(scores[0][0]);//85
         System.out.println(scores[0][1]);//96
         System.out.println(scores[0][2]);//85
         System.out.println(scores[0][3]);//75
         //System.out.println(scores[0][4]);//java.lang.ArrayIndexOutOfBoundsException: 4
     }
 }

5.二维数组的遍历

 for(inti=0; i<二维数组名.length; i++){ //二维数组对象.length
     for(intj=0; j<二维数组名[i].length; j++){//二维数组行对象.length
         System.out.print(二维数组名[i][j]);
     }
     System.out.println();
 }
 publicclassTest23TwoDimensionalArrayIterate {
     publicstaticvoidmain(String[] args) {
         //存储3个小组的学员的成绩,分开存储,使用二维数组。
         int[][] scores= {
                 {85,96,85,75},
                 {99,96,74,72,75},
                 {52,42,56,75}
         };
 ​
         System.out.println("一共有"+scores.length+"组成绩.");
         for (inti=0; i<scores.length; i++) {
             System.out.print("第"+ (i+1) +"组有"+scores[i].length+"个学员,成绩如下:");
             for (intj=0; j<scores[i].length; j++) {
                 System.out.print(scores[i][j]+"\t");
             }
             System.out.println();
         }
     }
 }

6.二维数组动态初始化

如果二维数组的每一个数据,甚至是每一行的列数,需要后期单独确定,那么就只能使用动态初始化方式了。动态初始化方式分为两种格式:

(1)规则二维表:每一行的列数是相同的
 //(1)确定行数和列数
 元素的数据类型[][] 二维数组名=new元素的数据类型[m][n];
     m:表示这个二维数组有多少个一维数组。或者说一共二维表有几行
     n:表示每一个一维数组的元素有多少个。或者说每一行共有一个单元格
 ​
 //此时创建完数组,行数、列数确定,而且元素也都有默认值
 ​
 //(2)再为元素赋新值
 二维数组名[行下标][列下标] =值;
 /*
 1 1 1 1 1
 2 2 2 2 2
 3 3 3 3 3
 4 4 4 4 4
 */
 publicclassTest24SameElementCount {
     publicstaticvoidmain(String[] args) {
         //1、声明二维数组,并确定行数和列数
         int[][] arr=newint[4][5];
 ​
         //2、确定元素的值
         for (inti=0; i<arr.length; i++) {
             for (intj=0; j<arr.length; j++) {
                 arr[i][j] =i+1;
             }
         }
 ​
         //3、遍历显示
         for(inti=0; i<arr.length; i++){
             for(intj=0; j<arr[i].length; j++){
                 System.out.print(arr[i][j] +" ");
             }
             System.out.println();
         }
     }
 }
(2)不规则:每一行的列数不一样
 //(1)先确定总行数
 元素的数据类型[][] 二维数组名=new元素的数据类型[总行数][];
 ​
 //此时只是确定了总行数,每一行里面现在是null
 ​
 //(2)再确定每一行的列数,创建每一行的一维数组
 二维数组名[行下标] =new元素的数据类型[该行的总列数];
 ​
 //此时已经new完的行的元素就有默认值了,没有new的行还是null
 ​
 //(3)再为元素赋值
 二维数组名[行下标][列下标] =值;
 /*
 1
 2 2
 3 3 3
 4 4 4 4
 5 5 5 5 5
 */
 publicclassTest25DifferentElementCount {
     publicstaticvoidmain(String[] args){
         //1、声明一个二维数组,并且确定行数
         //因为每一行的列数不同,这里无法直接确定列数
         int[][]  arr=newint[5][];
 ​
         //2、确定每一行的列数
         for(inti=0; i<arr.length; i++){
             /*
             arr[0] 的列数是1
             arr[1] 的列数是2
             arr[2] 的列数是3
             arr[3] 的列数是4
             arr[4] 的列数是5
             */
             arr[i] =newint[i+1];
         }
 ​
         //3、确定元素的值
         for(inti=0; i<arr.length; i++){
             for(intj=0; j<arr[i].length; j++){
                 arr[i][j] =i+1;
             }
         }
 ​
         //4、遍历显示
         for(inti=0; i<arr.length; i++){
             for(intj=0; j<arr[i].length; j++){
                 System.out.print(arr[i][j] +" ");
             }
             System.out.println();
         }
 ​
     }
 }

7.空指针异常

观察一下代码,运行后会出现什么结果。

 publicclassTest26NullPointerException {
     publicstaticvoidmain(String[] args) {
         //定义数组
         int[][] arr=newint[3][];
 ​
         System.out.println(arr[0][0]);//NullPointerException
     }
 }

因为此时数组的每一行还未分配具体存储元素的空间,此时arr[0]是null,此时访问arr[0][0]会抛出NullPointerException 空指针异常。

空指针异常在内存图中的表现

8.二维数组的内存图分析

二维数组本质上是元素类型是一维数组的一维数组。

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

         //1、声明二维数组,并确定行数和列数
         int[][] arr=newint[4][5];
         
         //2、确定元素的值
         for (inti=0; i<arr.length; i++) {
             for (intj=0; j<arr.length; j++) {
                 arr[i][j] =i+1;
             }
         }

         //1、声明一个二维数组,并且确定行数
         //因为每一行的列数不同,这里无法直接确定列数
         int[][]  arr=newint[5][];
         
         //2、确定每一行的列数
         for(inti=0; i<arr.length; i++){
             /*
             arr[0] 的列数是1
             arr[1] 的列数是2
             arr[2] 的列数是3
             arr[3] 的列数是4
             arr[4] 的列数是5
             */
             arr[i] =newint[i+1];
         }
         
         //3、确定元素的值
         for(inti=0; i<arr.length; i++){
             for(intj=0; j<arr[i].length; j++){
                 arr[i][j] =i+1;
             }
         }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/346366.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot集成kafka

选择特定版本spring-kafka官方使用文档 进入官网&#xff1a;https://spring.io/ 选择自己使用版本 点进去后按照 /2.7.0/reference/html/ 路径点进去就能看到想要版本的文档了

C++实现线程池

C实现线程池一、前言二、线程池的接口设计2.1、类封装2.2、线程池的初始化2.3、线程池的启动2.4、线程池的停止2.5、线程的执行函数run()2.6、任务的运行函数2.7、等待所有线程结束三、测试线程池四、源码地址总结一、前言 C实现的线程池&#xff0c;可能涉及以下知识点&#…

JVM - 垃圾回收

目录 垃圾回收基础和根搜索算法 垃圾回收概述 根搜索算法 引用分类 垃圾回收基础(跨代引用、记忆集、写屏障、判断垃圾的步骤、STW) 跨代引用 记忆集(Remembered Set) 写屏障 判断是否垃圾的步骤 GC类型 Stop-The-World 垃圾收集类型 判断类无用的条件 垃圾回收算…

Mybatis源码(2) - SqlSessionTemplate的介绍及创建过程

0. 前言1. Spring对SqlSessionTemplate的管理1.1. SqlSessionTemplate的创建&#xff1a;1.2. MapperProxy中sqlSession的来源&#xff1a;2. SqlSessionInterceptor中的getSqlSession0. 前言 众所周知&#x1f60f;:MyBatis通过SqlSessionFactory 创建SqlSession去调用Executo…

在VMware17 Pro中设置创建虚拟机Ubuntu 20

在VMware17 Pro中设置创建虚拟机Ubuntu 200 前言1 安装Ubuntu 20步骤0 前言 书接上回&#xff0c;安装好了VMware17 Pro之后&#xff0c;就是安装虚拟机了&#xff0c;前提是下好了系统安装包&#xff0c;以Ubuntu 20为例 1 安装Ubuntu 20步骤 首先点击创建新的虚拟机 新建…

利用NAS免费部署动态解析实现内网穿透

‍ 想要从外网访问家中的NAS等设备&#xff0c;一般来说我们需要知道家中路由器的公网IP。 现在固定的公网IP基本上很难免费申请到了&#xff0c;但是一般来说运营商可以免费提供一个动态变化的公网IP&#xff1a;当路由设备重启时&#xff0c;运营商会给你重新分配一个新的I…

PHP加载3D模型【WebGL】

这是另一篇关于如何使用 PHP加载 3D 模型的文章。 在这里&#xff0c;我使用 Laravel 作为后端及其存储。 我在前端使用 Three.Js 库来渲染和显示 3D 模型。 我将向您展示如何上传 3D 模型以及如何从 Laravel 存储加载 3D 模型。 请仔细完成以下步骤。 大家可以在评论区提出任何…

8Manage PPM项目管理系统独特的功能:项目完整性保护

项目有其内在复杂性&#xff08;项目管理的科学部分&#xff09;&#xff0c;这种复杂性可以进行划分和克服。项目也有人为的或偶然的复杂性&#xff08;项目管理的艺术部分&#xff09;&#xff0c;这种复杂性无法进行划分和克服。偶然的高复杂性会影响并使内在复杂性难以管理…

【系统架构设计师】计算机组成与体系结构 ① ( 计算机组成 | CPU | 存储器 | 总线 | IO 外设 | CPU 组成 | 运算器 | 控制器 )

文章目录一、计算机组成与体系结构二、计算机组成结构三、CPU 组成1、运算器2、控制器一、计算机组成与体系结构 计算机组成与体系结构 对应 大学的 计算机组成原理 课程 , 主要分为 : 计算机组成体系结构划分 两大知识板块 ; 在架构师考试时 , 平均分值是 3 分 ; 计算机组成…

三相可控全桥整流与DC Buck变换电路设计仿真问题汇总

目 录 问题 一、开关管没有打开的情况下&#xff0c;DC Buck输出负电压&#xff1f; 二、问题分析 1.输出端存在与母线电压反相的电压&#xff0c;因此可以确定为差模感应电压&#xff0c;如果输出端与母线端产生的是大小相等&#xff0c;方向相同的同相电压&#xff0c;则为共…

大数据框架之Hadoop:HDFS(六)DataNode(面试开发重点)

6.1DataNode工作机制 DataNode工作机制&#xff0c;如下图所示。 1&#xff09;一个数据块在DataNode上以文件形式存储在磁盘上&#xff0c;包括两个文件&#xff0c;一个是数据本身&#xff0c;一个是元数据包括数据块的长度&#xff0c;块数据的校验和&#xff0c;以及时间戳…

ROS笔记(4)——发布者Publisher与订阅者Subscribe的编程实现

发布者 以小海龟的话题消息为例,编程实现发布者通过/turtle1/cmd_vel 话题向 turtlesim节点发送消息&#xff0c;流程如图 步骤一 创建功能包&#xff08;工作空间为~/catkin_ws/src&#xff09; $ cd ~/catkin_ws/src $ catkin_create_pkg learning_topic roscpp rospy s…

FLStudio水果最新版本V21支持中文语言

FL Studio简称FL&#xff0c;全称&#xff1a;Fruity Loops Studio习惯叫它水果。软件现有版本是FLStudio21&#xff0c;已全面升级支持简体中文语言界面 。FL Studio 能让你的计算机就像是全功能的录音室一样&#xff0c;完成编曲、剪辑、录音、混音等工作&#xff0c;帮助爱好…

2023大厂高频软件测试面试真题(附答案)

一、接口测试面试题&#xff1a;1.接口测试是怎么做的&#xff0c;如何分析数据&#xff1f;接口测试实际跟一般测试不同就是测试用例的设计部分。获取接口规范。设计接口测试功能用例&#xff08;主要从用户角度出发看接口能否实现业务需求&#xff0c;用例设计就是黑盒用例那…

2022财年净新增1159家门店,百胜中国门店高速扩张背后有何阳谋?

中国最大餐饮企业百胜中国控股有限公司&#xff08;下称“百胜中国”&#xff09;&#xff0c;2月8日发布了2022年度第四季度及全年未经审核的财务业绩。 从财报数据来看&#xff0c;这家拥有肯德基、必胜客、黄记煌等诸多餐饮品牌的巨头&#xff0c;已经顺利渡过了疫情笼罩下…

计算机网络(第7版)第五章(物理层)知识点整理

计算机网络 参考书目&#xff1a;《计算机网络&#xff08;第7版&#xff09;》&#xff1a;谢希仁——电子工业出版社 《精通Windows Sockets网络开发--基于Visual C实现》&#xff1a;孙海民——人民邮电出版社 第五章&#xff1a;物理层计算机网络一、基本概念二、传输媒体…

和数集团打造《神念无界:源起山海》,诠释链游领域创新与责任

首先&#xff0c;根据网上资料显示&#xff0c;一部《传奇》&#xff0c;二十年热血依旧。 《传奇》所缔造的成绩&#xff0c;承载的是多少人的青春回忆&#xff0c;《传奇》无疑已经在游戏史上写下了浓墨重彩的一笔。 相比《传奇》及背后的研发运营公司娱美德名声大噪&#x…

uniapp上高德(百度)地图API的使用(APP安卓)

前言由于在app中没有document,window等对象&#xff0c;所以使用在pc端传统方法引入的方式&#xff0c;将会发现无法引用成功&#xff0c;会出现白屏现象。目前有两种解决方式&#xff1a;使用uniapp的web-view方式&#xff08;百度地图&#xff09;使用renderjs来调用document…

【51媒体网】媒体邀约行业诞生及其前景预测

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。一&#xff0c;媒体邀约行业的诞生媒体邀约行业是随着现代社会媒体的普及而逐渐形成的。随着互联网和社交媒体的快速发展&#xff0c;媒体作为信息传播的重要渠道之一&#xff0c;越来越成…

PPP协议实验及配置

PPP协议实验拓扑图PPP认证配置PAP认证CHAP认证接口地址不在一个网段&#xff1f;地址自动协商通过IPCP方式获取到默认路由拓扑图 首先在设备上增添两个Serial接口&#xff1a; PPP认证配置 PAP认证 AR1作为认证方&#xff0c;AR2作为被认证方&#xff1a; AR1&#xff1a…