第5章 面向对象基础(上)

学习目标

  • 初步了解面向对象的思想
  • 能够明确类与对象关系
  • 能够掌握类的定义格式
  • 能够掌握创建对象格式
  • 理解包的作用
  • 掌握包的声明和导入
  • 能够通过类访问类的静态成员变量和静态成员方法
  • 能够通过对象访问对象的非静态成员变量和非静态成员方法
  • 能够区别静态方法和非静态方法
  • 能够区别类变量与实例变量
  • 能够区别成员变量与局部变量
  • 能够理解方法的调用执行机制
  • 能够理解方法的参数传递机制
  • 掌握方法的可变参数的使用
  • 掌握方法重载的概念
  • 能够判断出方法的重载
  • 了解命令行参数
  • 理解递归方法
  • 理解对象数组

第五章 面向对象基础(上)

5.1 面向对象思想概述

1、概述

Java语言是一种面向对象的程序设计语言,而面向对象思想(OOP)是一种程序设计思想,我们在面向对象思想的指引下,使用Java语言去设计、开发计算机程序。
这里的对象泛指现实中一切事物,每种事物都具备自己的属性行为。面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。
它区别于面向过程思想(POP),强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。

2、面向对象与面向过程的区别

面向过程:POP: Process-Oriented Programming

  1. 以函数(方法)为最小单位
  2. 数据独立于函数之外
  3. 以过程,步骤为主,考虑怎么做

面向对象:OOP: Object Oriented Programming

  1. 以类/对象为最小单位,类包括:数据+方法
  2. 以对象(谁)为主,考虑谁来做,谁能做

面向对象仍然包含面向过程,只不过关注点变了,关注谁来做

程序员的角色:

面向过程:程序员是具体执行者

面向对象:程序员是指挥者

面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。

例子:把大象装进冰箱

第5章 面向对象基础(上) - 图1

3、面向对象的基本特征

面向对象的语言中,包含了三大基本特征,即封装、继承和多态。

5.2 类和对象

环顾周围,你会发现很多对象,比如桌子,椅子,同学,老师等。桌椅属于办公用品,师生都是人类。那么什么是类呢?什么是对象呢?

什么是类

  • :是一类具有相同特性的事物的抽象描述,是一组相关属性行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。

  • 类可以看做是一个模版,或者图纸,系统根据类的定义来造出对象。我们要造一个汽车,怎么样造?类就是这个图纸,规定了汽车的详细信息,然后根据图纸将汽车造出来。
    类:我们叫做class。 对象:我们叫做Object,instance(实例)。以后我们说某个类的对象,某个类的实例。是一样的意思。
    示例1:
    英雄联盟、王者荣耀中的类和对象
    第5章 面向对象基础(上) - 图2
    英雄就是类,具体的英雄,盖伦、提莫是对象。
    示例2:
    月饼模具和月饼
    月饼模具是类,使用月饼模具制作的一个个月饼就是对象
    第5章 面向对象基础(上) - 图3

现实中,描述一类事物:

  • 属性:就是该事物的状态信息。
  • 行为:就是该事物能够做什么。

举例:小猫。

  1. 属性:名字、体重、年龄、颜色。<br />
  2. 行为:走、跑、叫。

什么是对象

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

现实中,一类事物的一个实例:一只小猫 。

举例:一只小猫。

  1. 属性:tom5kg2 yearsyellow。<br />
  2. 行为:溜墙根走、蹦跶的跑、喵喵叫。

类与对象的关系

  • 类是对一类事物的描述,是抽象的
  • 对象是一类事物的实例,是具体的
  • 类是对象的模板,对象是类的实体

第5章 面向对象基础(上) - 图4

5.3 类的定义和对象的创建

事物与类的对比

现实世界的一类事物:

  1. **属性**:事物的状态信息。<br />
  2. **行为**:事物能够做什么。

Java中用class描述事物也是如此:

  1. **成员变量**:对应事物的**属性**<br />
  2. **成员方法**:对应事物的**行为**

类的定义格式

  1. public class ClassName {
  2. //成员变量
  3. //成员方法
  4. }
  • 定义类:就是定义类的成员,包括成员变量成员方法
  • 成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外
  • 成员方法:和以前写的main方法格式类似。只不过功能和形式更丰富了。在类中,方法外。

类的定义格式举例:

  1. public class Person {
  2. //成员变量
  3. String name//姓名
  4. int age//年龄
  5. boolean isMarried;
  6. public void walk(){
  7. System.out.println("人走路...");
  8. }
  9. public String display(){
  10. return "名字是:" + name + ",年龄是:" + age + ",Married:" + isMarried;
  11. }
  12. }

第5章 面向对象基础(上) - 图5

对象的创建

创建对象:

  1. new 类名()//也称为匿名对象
  2. //给创建的对象命名
  3. //或者说,把创建的对象用一个引用数据类型的变量保存起来
  4. 类名 对象名 = new 类名();

类似于:

  1. System.out.println("高老师年龄是:" + 18);//如果确定只在这里一次性使用,那么可以不用变量保存(#^.^#)
  2. //把18用int类型的age变量保存起来,方便后面使用
  3. int age = 18;
  4. System.out.println("高老师年龄是:" + age);
  5. System.out.println("仓老师比高老师大10岁,年龄是:" + (age+10));

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

  1. class Student{
  2. }
  3. public class TestStudent{
  4. //Java程序的入口
  5. public static void main(String[] args){
  6. System.out.println(new Student());//Student@7852e922
  7. Student stu = new Student();
  8. System.out.println(stu);//Student@4e25154f
  9. int[] arr = new int[5];
  10. System.out.println(arr);//[I@70dea4e
  11. }
  12. }
  13. //Student和TestStudent没有位置要求,谁在上面谁在下面都可以
  14. //但是如果TestStudent类的main中使用了Student类,那么要求编译时,这个Student已经写好了,不写是不行的
  15. //如果两个类都在一个.java源文件中,只能有一个类是public的

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

那么像“Student@4e25154f”是对象的地址吗?不是,因为Java是对程序员隐藏内存地址的,不暴露内存地址信息,所以打印对象时不直接显示内存地址,而是JVM提取了对象描述信息给你现在,默认提取的是对象的运行时类型@代表对象唯一编码的hashCode值。

第5章 面向对象基础(上) - 图6

5.4 包(Package)

1、包的作用

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

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

例如:

  • 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)。

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

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

2、声明包的语法格式

  1. package 包名;

注意:

(1)必须在源文件的代码首行

(2)一个源文件只能有一个声明包的语句

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

例如:com.atguigu.xxx;

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

3、如何跨包使用类

前提:被使用的类或成员的权限修饰符是>缺省的,即可见的

(1)使用类型的全名称

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

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

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

import语句的语法格式:

  1. import 包.类名;
  2. import 包.*;
  3. import static 包.类名.静态成员; //后面再讲

注意:

使用java.lang包下的类,不需要import语句,就直接可以使用简名称

import语句必须在package下面,class的上面

当使用两个不同包的同名类时,例如:java.util.Date和java.sql.Date。一个使用全名称,一个使用简名称

示例代码:

  1. package com.atguigu.bean;
  2. public class Student {
  3. // 成员变量
  4. private String name;
  5. private int age;
  6. // 构造方法
  7. public Student() {
  8. }
  9. public Student(String name, int age) {
  10. this.name = name;
  11. this.age = age;
  12. }
  13. // 成员方法
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public String getName() {
  18. return name;
  19. }
  20. public void setAge(int age) {
  21. this.age = age;
  22. }
  23. public int getAge() {
  24. return age;
  25. }
  26. }
  1. package com.atguigu.test;
  2. import java.util.Scanner;
  3. import java.util.Date;
  4. import com.atguigu.bean.Student;
  5. public class Test{
  6. public static void main(String[] args){
  7. Scanner input = new Scanner(System.in);
  8. Student stu = new Student();
  9. String str = "hello";
  10. Date now = new Date();
  11. java.sql.Date d = new java.sql.Date(346724566);
  12. }
  13. }

5.5 成员变量

1、成员变量的分类

实例变量:没有static修饰,也叫对象属性,属于某个对象的,通过对象来使用

类变量:有static修饰,也叫类变量,属于整个类的,不是属于某个实例

2、如何声明成员变量?

  1. 【修饰符】 class 类名{
  2. 【修饰符】 数据类型 属性名; //属性有默认值
  3. 【修饰符】 数据类型 属性名 = 值; //属性有初始值
  4. }

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

例如:声明一个中国人的类

  1. class Chinese{
  2. static String country;
  3. String name;
  4. char gender = '男';//显式赋值
  5. }

3、如何在类外面访问成员变量?

(1)类变量

  1. 类名.静态成员变量 //推荐
  2. 对象名.静态成员变量 //不推荐

(2)实例变量

  1. 对象名.静态成员变量 //只能使用这种方式

例如:

  1. public class TestChinese {
  2. public static void main(String[] args) {
  3. //类名.静态成员变量
  4. System.out.println(Chinese.country);
  5. //错误,非静态成员变量必须通过对象.进行访问
  6. // System.out.println(Chinese.name);
  7. Chinese c1 = new Chinese();
  8. //对象名.非静态成员变量
  9. System.out.println(c1.name);
  10. //静态的成员变量也可以通过对象.进行访问
  11. //对象名.非静态成员变量
  12. System.out.println(c1.country);
  13. System.out.println(c1.gender);
  14. }
  15. }
  16. class Chinese{
  17. static String country;
  18. String name;
  19. char gender = '男';
  20. }

4、成员变量的特点

(1)成员变量有默认值

基本类型 整数(byte,short,int,long) 0
浮点数(float,double) 0.0
字符(char) ‘\u0000’
布尔(boolean) false
数据类型 默认值
引用类型 数组,类,接口 null

(2)类变量的值是所有对象共享的,而实例变量的值是每个对象独立的

  1. public class TestChinese {
  2. public static void main(String[] args) {
  3. Chinese c1 = new Chinese();
  4. Chinese c2 = new Chinese();
  5. c1.name = "张三";
  6. c2.name = "李四";
  7. c2.gender = '女';
  8. // c1.country = "中国";
  9. Chinese.country = "中国";//推荐
  10. System.out.println("c1.country = " + c1.country + ",c1.name = " + c1.name + ",c1.gender = " + c1.gender);
  11. System.out.println("c2.country = " + c2.country + ",c2.name = " + c2.name + ",c2.gender = " + c2.gender);
  12. }
  13. }
  14. class Chinese{
  15. static String country;
  16. String name;
  17. char gender = '男';
  18. }

5、成员变量的内存图

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

第5章 面向对象基础(上) - 图7

区域名称 作用
程序计数器 程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址
本地方法栈 当程序中调用了native的本地方法时,本地方法执行期间的内存区域
方法区 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
堆内存 存储对象(包括数组对象),new来创建的,都存储在堆内存。
虚拟机栈 用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型、对象引用,方法执行完,自动释放。
  1. class Test08FieldSave{
  2. public static void main(String[] args){
  3. Chinese c1 = new Chinese();
  4. c1.name = "张三";
  5. System.out.println(c1.country);//静态变量,也可以使用"对象名."进行访问
  6. System.out.println(c1.name);//非静态的实例变量通过"对象名."进行访问
  7. Chinese c2 = new Chinese();
  8. c2.name = "李四";
  9. System.out.println(c2.country);
  10. System.out.println(c2.name);
  11. System.out.println("--------------------------------------");
  12. //其中一个对象将静态变量的值修改了,其他对象都会改变
  13. //因为静态变量只存一份
  14. c1.country = "中华人民共和国";
  15. System.out.println(c1.country);
  16. System.out.println(c2.country);
  17. System.out.println(Chinese.country);
  18. //其中一个对象将非静态实例变量修改了,其他对象不受影响
  19. c1.name = "张三丰";
  20. System.out.println(c1.name);
  21. System.out.println(c2.name);
  22. }
  23. }
  24. class Chinese{
  25. static String country = "中国";//静态变量,所有中国人的国家的名称是一样,只需要存储一份
  26. String name;//实例变量,每一个中国人的姓名是独立,每一个对象单独存储
  27. }

第5章 面向对象基础(上) - 图8

  1. class MyDate{
  2. int year;
  3. int month;
  4. int day;
  5. }
  6. class Employee{
  7. String name;
  8. MyDate birthday;
  9. }
  10. class Test09FieldExer3{
  11. public static void main(String[] args){
  12. //创建两个员工对象
  13. Employee e1 = new Employee();
  14. Employee e2 = new Employee();
  15. //为两个员工对象的成员变量赋值
  16. e1.name = "张三";
  17. e1.birthday = new MyDate();
  18. e2.name = "李四";
  19. e2.birthday = new MyDate();
  20. e1.birthday.year = 2000;
  21. e1.birthday.month = 1;
  22. e1.birthday.day = 1;
  23. e2.birthday.year = 2000;
  24. e2.birthday.month = 3;
  25. e2.birthday.day = 8;
  26. System.out.println("第一个员工,姓名:" + e1.name +",生日:" + e1.birthday.year + "年" + e1.birthday.month + "月" + e1.birthday.day + "日");
  27. System.out.println("第二个员工,姓名:" + e2.name +",生日:" + e2.birthday.year + "年" + e2.birthday.month + "月" + e2.birthday.day + "日");
  28. }
  29. }

第5章 面向对象基础(上) - 图9

6、成员变量练习题

(1)声明一个圆的图形类,有属性:半径
在测试类的main中,创建圆的2个对象,为半径属性赋值,并显示两个圆的半径值和面积值
提示:圆周率为Math.PI

(2)声明一个银行账户类,有属性:利率、账号、余额

  1. 在测试类的main中,创建账户类的两个对象,其中所有账户的利率是相同的,都是0.035,而账号和余额是不同的,并打印显示

(3)声明一个MyDate类型,有属性:年,月,日

  1. 声明另一个Employee类型,有属性:姓名(String类型),生日(MyDate类型)

在测试类中的main中,创建两个员工对象,并为他们的姓名和生日赋值,并显示

5.6 成员方法

成员变量是用来存储对象的数据信息的,那么如何表示对象的行为功能呢?就要通过方法来实现

5.6.1 方法的概念

方法也叫函数,是一个独立功能的定义,是一个类中最基本的功能单元。

把一个功能封装为方法的目的是,可以实现代码重用,从而简少代码量。

5.6.2 方法的原则

方法的使用原则:

(1)必须先声明后使用

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

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

5.6.3 成员方法的分类

成员方法分为两类:

  • 实例方法:没有static修饰的方法,必须通过实例对象来调用。
  • 静态方法:有static修饰的方法,也叫类方法,可以由类名来调用。

5.6.4 如何声明方法

1、方法声明的位置必须在类中方法外

2、语法格式

  1. 【修饰符】 返回值类型 方法名(【参数列表:参数类型1 参数名1,参数类型2 参数名, ...... 】){
  2. 方法体;
  3. return 返回值;】
  4. }
  • 修饰符: 修饰符后面一一介绍,例如:public,static等都是修饰符
  • 返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者

    • 基本数据类型
    • 引用数据类型
    • 无返回值类型:void
  • 方法名:给方法起一个名字,见名知意,能准确代表该方法功能的名字
  • 参数列表:方法内部需要用到其他方法中的数据,需要通过参数传递的形式将数据传递过来,可以是基本数据类型、引用数据类型、也可以没有参数,什么都不写
  • 方法体:特定功能代码
  • return:结束方法,并将方法的结果返回去,

    • 如果返回值类型不是void,方法体中必须保证一定有return 返回值;语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容。
    • 如果返回值类型为void时,return 后面不用跟返回值,甚至也可以没有return语句。
    • return语句后面就不能再写其他代码了,否则会报错:Unreachable code

声明位置示例:

  1. 类{
  2. 方法1(){
  3. }
  4. 方法2(){
  5. }
  6. }

错误示例:

  1. 类{
  2. 方法1(){
  3. 方法2(){ //位置错误
  4. }
  5. }
  6. }

示例一:

声明一个圆的图形类:

  1. 属性(成员变量):半径,
  2. 成员方法:求面积的方法,返回圆对象信息的方法
  3. 在测试类的main中,创建圆的2个对象,为半径属性赋值,调用两个方法进行测试<br />
  4. 提示:圆周率为Math.PI
  1. class Circle{
  2. double radius;
  3. double area() {
  4. return Math.PI * radius * radius;
  5. }
  6. }

Circle不同的对象,半径值不同,那么面积也不同,所以这里area()是非静态的

示例二:

声明一个计算工具类CountTools:

  1. 方法1:求两个整数的最大值
  1. class CountTools{
  2. static int max(int a, int b) {
  3. return a > b ? a : b;
  4. }
  5. }

CountTools只是一个工具类,求两个整数最大值的功能,和CountTools对象无关,所以这里max方法声明为静态的更好,当然也可以声明为非静态的,就是调用的时候需要创建CountTools对象而已。

5.6.5 如何在其他类中调用方法

(1)实例方法

  1. 对象名.实例方法(【实参列表】) //必须通过对象来访问

示例代码:

  1. public class TestCircle {
  2. public static void main(String[] args) {
  3. Circle c1 = new Circle();
  4. c1.radius = 1.2;
  5. System.out.println("c1的面积:" + c1.area());
  6. //非静态方法只能通过"对象."进行访问
  7. // System.out.println("c1的面积:" + Circle.area());
  8. Circle c2 = new Circle();
  9. c2.radius = 2.5;
  10. System.out.println("c2的面积:" + c2.area());
  11. }
  12. }
  13. class Circle{
  14. double radius;
  15. public double area() {
  16. return Math.PI * radius * radius;
  17. }
  18. }

(2)类方法

  1. 类名.类方法(【实参列表】) //推荐
  2. 对象名.类方法(【实参列表】) //不推荐

示例:

  1. public class TestCount {
  2. public static void main(String[] args) {
  3. System.out.println(CountTools.max(4, 1));
  4. //静态方法也可以通过“对象.”访问,就是麻烦点
  5. CountTools c = new CountTools();
  6. System.out.println(c.max(2, 5));
  7. }
  8. }
  9. class CountTools{
  10. static int max(int a, int b) {
  11. return a > b ? a : b;
  12. }
  13. }

(3)总结

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

总结:

(1)调用时,需要传“实参”,实参的个数、类型、顺序顺序要与形参列表一一对应

  1. 如果方法没有形参,就不需要也不能传实参。

(2)调用时,如果方法有返回值,可以接受或处理返回值结果,当然也可以不接收,那么此时返回值就丢失了。

  1. 如果方法的返回值类型是void,不需要也不能接收和处理返回值结果。

5.6.6 在本类中访问本类的成员变量和成员方法

直接用,不需要加“对象名.”和”类名.”

唯一例外:静态方法中不能直接访问本类的非静态的成员变量和成员方法

  1. class Circle{
  2. double radius;
  3. //写一个方法,可以返回“圆对象”的详细信息
  4. String getDetailInfo(){
  5. return "半径:" + radius + ",面积:" + area() +",周长:" + perimeter();
  6. }
  7. //写一个方法,可以返回“圆对象”的面积
  8. double area(){
  9. return Math.PI*radius*radius;
  10. }
  11. //写一个方法,可以返回“圆对象”的周长
  12. double perimeter(){
  13. return 2*Math.PI*radius;
  14. }
  15. }
  1. class Test{
  2. static void test(){
  3. System.out.println("");
  4. }
  5. void method(){
  6. test();
  7. }
  8. public static void main(String[] args){
  9. method();//错误
  10. test();//正确
  11. }
  12. }

5.6.7 方法的声明与调用练习

1、声明数学工具类MathTools

(1)静态方法1:可以比较两个整数是否相同
(2)静态方法2:可以判断某个数是否是素数
(3)静态方法3:可以返回某个整数所有的约数(约数:从1到这个数之间所有能把它整除的数)
在Test测试类的main中调用测试

2、声明数组工具类ArraysTools

(1)静态方法1:可以实现给任意整型数组实现从小到大排序
(2)静态方法2:可以遍历任意整型数组,返回结果效果:[元素1,元素2,元素3。。。]

3、声明矩形类

(1)包含属性:长、宽

(2)包含3个方法:

  1. 求面积、
  2. 求周长、
  3. 返回矩形对象的信息:长:xx,宽:xx,面积:xx,周长:xx

4、声明一个圆类,有半径radius成员变量

  1. 声明一个图形工具类GraphicTools,包含一个静态方法可以返回两个圆中面积大的那一个圆的方法
  2. 在测试类中测试

5.6.8 方法调用内存分析

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

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

示例一:

  1. public class TestCount {
  2. public static void main(String[] args) {
  3. int a = 4;
  4. int b = 2;
  5. int m = CountTools.max(a, b));
  6. }
  7. }
  8. class CountTools{
  9. static int max(int a, int b) {
  10. return a > b ? a : b;
  11. }
  12. }

第5章 面向对象基础(上) - 图10

示例二:

  1. public class TestCircle {
  2. public static void main(String[] args) {
  3. Circle c1 = new Circle();
  4. c1.radius = 1.2;
  5. int area1 = c1.area();
  6. Circle c2 = new Circle();
  7. c2.radius = 2.5;
  8. int area2 = c2.area();
  9. }
  10. }
  11. class Circle{
  12. double radius;
  13. public double area() {
  14. return Math.PI * radius * radius;
  15. }
  16. }

第5章 面向对象基础(上) - 图11

示例三:

  1. public class Test {
  2. public static void main(String[] args) {
  3. int[] arr = {2,4,1,5,3};
  4. ArrayUtil.sort(arr);
  5. for (int i = 0; i < arr.length; i++) {
  6. System.out.println(arr[i]);
  7. }
  8. }
  9. }
  10. class ArrayUtil{
  11. public static void sort(int[] arr){
  12. for (int i = 1; i < arr.length; i++) {
  13. for (int j = 0; j < arr.length - i; j++) {
  14. if(arr[j] > arr[j+1]){
  15. int temp = arr[j];
  16. arr[j] = arr[j+1];
  17. arr[j+1] = temp;
  18. }
  19. }
  20. }
  21. }
  22. }

第5章 面向对象基础(上) - 图12

5.6.9 方法的参数传递机制

  • 方法的参数传递机制:实参给形参赋值

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

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

示例代码1:

  1. class Test{
  2. public static void swap(int a, int b){
  3. int temp = a;
  4. a = b;
  5. b = temp;
  6. }
  7. public static void mainString[] args){
  8. int x = 1;
  9. int y = 2;
  10. swap(x,y);//调用完之后,x与y的值不变
  11. }
  12. }

示例代码2:

  1. class Test{
  2. public static void change(MyData my){
  3. my.num *= 2;
  4. }
  5. public static void main(String[] args){
  6. MyData m = new MyData();
  7. m.num = 1;
  8. change(m);//调用完之后,m对象的num属性值就变为2
  9. }
  10. }
  11. class MyData{
  12. int num;
  13. }

示例代码3:

  1. public class Test {
  2. public static void main(String[] args) {
  3. int[] arr = {2,4,1,5,3};
  4. ArrayUtil.sort(arr);
  5. for (int i = 0; i < arr.length; i++) {
  6. System.out.println(arr[i]);
  7. }
  8. }
  9. }
  10. class ArrayUtil{
  11. public static void sort(int[] arr){
  12. for (int i = 1; i < arr.length; i++) {
  13. for (int j = 0; j < arr.length - i; j++) {
  14. if(arr[j] > arr[j+1]){
  15. int temp = arr[j];
  16. arr[j] = arr[j+1];
  17. arr[j+1] = temp;
  18. }
  19. }
  20. }
  21. }
  22. }

陷阱1:

  1. /*
  2. 陷阱1:在方法中,形参 = 新new对象,那么就和实参无关了
  3. */
  4. class Test{
  5. public static void change(MyData my){
  6. my = new MyData();//形参指向了新对象
  7. my.num *= 2;
  8. }
  9. public static void main(String[] args){
  10. MyData m = new MyData();
  11. m.num = 1;
  12. change(m);//调用完之后,m对象的num属性值仍然为1
  13. }
  14. }
  15. class MyData{
  16. int num;
  17. }

陷阱2:见字符串和包装类部分

  1. public class Test {
  2. public static void main(String[] args) {
  3. StringUtil util = new StringUtil();
  4. String str = "尚硅谷";
  5. util.change(str);
  6. System.out.println(str);
  7. }
  8. }
  9. class StringUtil{
  10. public void change(String str){
  11. str += "你好";//String对象不可变,一旦修改就会产生新对象
  12. }
  13. }

5.6.10 成员变量与局部变量的区别

1、变量的分类

  • 成员变量

    • 静态变量
    • 实例变量
  • 局部变量

2、区别

1、声明位置和方式
(1)静态变量:在类中方法外,并且有static修饰
(2)实例变量:在类中方法外,没有static修饰
(3)局部变量:在方法体{}中或方法的形参列表、代码块中

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

3、生命周期
(1)静态变量:和类的生命周期一样,因为它的值是该类所有对象共享的,早于对象的创建而存在。
(2)实例变量:和对象的生命周期一样,随着对象的创建而存在,随着对象被GC回收而消亡,
而且每一个对象的实例变量是独立的。
(3)局部变量:和方法调用的生命周期一样,每一次方法被调用而在存在,随着方法执行的结束而消亡,
而且每一次方法调用都是独立。

4、作用域
(1)静态变量和实例变量:不谈作用域
在本类中,唯一的限制,静态方法或静态代码块中不能使用非静态的,其他都可以直接使用。
在其他类中,能不能使用看修饰符(public,protected,private等)
(2)局部变量:有作用域
出了作用域就不能使用

5、修饰符(后面来讲)
(1)静态变量:很多
public,protected,private,final,volatile等,一定有的是static
(2)实例变量
public,protected,private,final,volatile,transient等
(3)局部变量
final

public,protected,private:权限修饰符
final:是否是常量,即值是否可以修改
volatile:和多线程有关
transient:是否序列化,和IO有关

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

5.6 可变参数

JDK1.5之后,如果我们定义一个方法时,此时某个形参的类型可以确定,但是形参的个数不确定,那么我们可以使用可变参数。

格式:

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

要求:

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

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

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

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

示例一: 求n个整数的和

  1. public class ChangeArgs {
  2. public static void main(String[] args) {
  3. int[] arr = { 1, 4, 62, 431, 2 };
  4. int sum1 = getSum1(arr);
  5. System.out.println(sum1);
  6. int sum2 = getSum2(arr);
  7. System.out.println(sum2);
  8. int sum3 = getSum2(1, 4, 62, 431, 2);
  9. System.out.println(sum3);
  10. }
  11. // 完成数组 所有元素的求和
  12. // 原始写法
  13. public static int getSum1(int[] arr) {
  14. int sum = 0;
  15. for (int i = 0; i < arr.length; i++) {
  16. sum += arr[i];
  17. }
  18. return sum;
  19. }
  20. // 可变参数写法
  21. public static int getSum2(int... arr) {
  22. int sum = 0;
  23. for (int i = 0; i < arr.length; i++) {
  24. sum += arr[i];
  25. }
  26. return sum;
  27. }
  28. }

示例二:求1-n个整数中的最大值

  1. public class ChangeArgs_Exer1 {
  2. public static void main(String[] args) {
  3. System.out.println(max(1));
  4. System.out.println(max(5,3,2,6));
  5. }
  6. public static int max(int num, int... others){
  7. int max = num;
  8. for (int i = 0; i < others.length; i++) {
  9. if(max < others[i]){
  10. max = num;
  11. }
  12. }
  13. return max;
  14. }
  15. }

示例三:字符串拼接

需求一:返回n个字符串拼接结果,如果没有传入字符串,那么返回空字符串””

  1. public class ChangeArgs_Exer2 {
  2. public static void main(String[] args) {
  3. System.out.println(concat());
  4. System.out.println(concat("hello","world"));
  5. }
  6. public static String concat(String... args){
  7. String str = "";
  8. for (int i = 0; i < args.length; i++) {
  9. str += args[i];
  10. }
  11. return str;
  12. }
  13. }

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

  1. public class ChangeArgs_Exer4 {
  2. public static void main(String[] args) {
  3. System.out.println(concat('+'));
  4. System.out.println(concat('+',"hello","world"));
  5. }
  6. public static String concat(char seperator, String... args){
  7. String str = "";
  8. for (int i = 0; i < args.length; i++) {
  9. if(i==0){
  10. str += args[i];
  11. }else{
  12. str += seperator + args[i];
  13. }
  14. }
  15. return str;
  16. }
  17. }

课后练习

1、声明一个方法,可以找出任意个整数的最大公约数

2、声明一个方法,可以找出任意个字符串中的公共字符,例如:hello与world的公共字符是o和l,如果没有就返回””

提示:获取字符串长度的方法:int length()

  1. 例如:字符串.length(), "hello".length()返回5
  2. 获取字符串[index]位置的字符:char charAt(int index)
  3. 例如:字符串.charAt(index) hello.charAt(1)返回'e'

参考答案:

  1. class Test12MethodExer1{
  2. public static void main(String[] args){
  3. System.out.println(maxYue(6,9));
  4. System.out.println(maxYue(16,18,4,8));
  5. }
  6. public static int maxYue(int... args){
  7. //找很多个数的公约数
  8. //(1)找出它们中最小的
  9. //类似于在数组中找最小值
  10. int min = args[0];
  11. for(int i=1; i<args.length; i++){
  12. if(args[i] < min){
  13. min = args[i];
  14. }
  15. }
  16. //(2)从小的数往1的方向找,找到的第一个公约数就是它们的最大公约数
  17. for(int i=min; i>=1; i--){
  18. //这个i得把args中所有的数都整除了,那么i就是他们的公约数
  19. boolean flag = true;//假设i可以把args中所有数都整除了
  20. for(int j=0; j<args.length; j++){
  21. if(args[j] % i !=0){//args中有一个数不能被i整除,说明这个i不是它们的公约数
  22. flag = false;
  23. break;
  24. }
  25. }
  26. if(flag){
  27. return i;//return会结束当前方法
  28. }
  29. }
  30. return 1;//1是所有数的公约数
  31. /*
  32. 假设args中6和9 args[0]是6,args[1]是9,min=6
  33. 外循环第一次i=min=6,
  34. 内循环第一次:j=0, if(args[0] % 6!=0)不成立 j++
  35. 内循环第二次:j=1, if(args[1] % 6!=0)成立 flag = false ;break;
  36. 说明i不是它们公约数
  37. 外循环第二次i=5
  38. 内循环第一次:j=0, if(args[0] % 5 !=0)成立 j++ flag = false;break;
  39. 说明i不是它们公约数
  40. 外循环第三次i=4
  41. 内循环第一次:j=0, if(args[0] % 4 !=0)成立 j++ flag = false;break;
  42. 说明i不是它们公约数
  43. 外循环第四次i=3
  44. 内循环第一次:j=0, if(args[0] % 3 !=0)不成立 j++
  45. 内循环第二次:j=1, if(args[1] % 3 !=0)不成立 j++
  46. if(flag)成立,return i;
  47. */
  48. }
  49. }
  1. public static void main(String[] args) {
  2. // 声明一个方法,可以找出任意个整数的最大公约数
  3. int maxYueShu = getMaxYueShu(10, 20, 15, 20);
  4. System.out.println("maxYueShu = " + maxYueShu);
  5. System.out.println(sameChars("hello", "world"));//ol
  6. System.out.println(sameChars("chai", "wolrd"));
  7. System.out.println(sameChars("atguigu", "java"));
  8. System.out.println(sameChars("samewordsg", "string", "words"));
  9. }
  10. public static String sameChars(String... words) {
  11. String same = "";
  12. for (char a = 'A'; a <= 'z'; a++) {
  13. //统计相同单词的个数
  14. int count = 0;
  15. for (int j = 0; j < words.length; j++) {
  16. for (int h = 0; h < words[j].length(); h++) {
  17. if (words[j].charAt(h) == a) {
  18. count++;
  19. break;
  20. }
  21. }
  22. }
  23. //当相同的个数等于字符个数时 判定所有的字符串都出现过
  24. if (count == words.length) {
  25. same += a;
  26. }
  27. }
  28. return same;
  29. }

5.7 方法重载

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

示例一:比较两个数据是否相等

比较两个数据是否相等。参数类型分别为两个byte类型,两个short类型,两个int类型,两个long类型,并在main方法中进行测试。

  1. public class Method_Demo6 {
  2. public static void main(String[] args) {
  3. //定义不同数据类型的变量
  4. byte a = 10;
  5. byte b = 20;
  6. short c = 10;
  7. short d = 20;
  8. int e = 10;
  9. int f = 10;
  10. long g = 10;
  11. long h = 20;
  12. // 调用
  13. System.out.println(compare(a, b));
  14. System.out.println(compare(c, d));
  15. System.out.println(compare(e, f));
  16. System.out.println(compare(g, h));
  17. }
  18. // 两个byte类型的
  19. public static boolean compare(byte a, byte b) {
  20. System.out.println("byte");
  21. return a == b;
  22. }
  23. // 两个short类型的
  24. public static boolean compare(short a, short b) {
  25. System.out.println("short");
  26. return a == b;
  27. }
  28. // 两个int类型的
  29. public static boolean compare(int a, int b) {
  30. System.out.println("int");
  31. return a == b;
  32. }
  33. // 两个long类型的
  34. public static boolean compare(long a, long b) {
  35. System.out.println("long");
  36. return a == b;
  37. }
  38. }

示例二:求各种最大值

用重载实现:
定义方法求两个整数的最大值
定义方法求三个整数的最大值
定义方法求两个小数的最大值

  1. //求两个整数的最大值
  2. public int max(int a,int b){
  3. return a>b?a:b;
  4. }
  5. //求三个整数的最大值
  6. public int max(int a, int b, int c){
  7. return max(max(a,b),c);
  8. }
  9. //求两个小数的最大值
  10. public double max(double a, double b){
  11. return a>b?a:b;
  12. }

示例三:判断两个方法是否是合理的重载方法

  1. //判断如下两个方法是否构成重载:是
  2. class StringUtil{
  3. public static String concat(char seperator, String... args){
  4. String str = "";
  5. for (int i = 0; i < args.length; i++) {
  6. if(i==0){
  7. str += args[i];
  8. }else{
  9. str += seperator + args[i];
  10. }
  11. }
  12. return str;
  13. }
  14. public static String concat(String[] args){
  15. String str = "";
  16. for (int i = 0; i < args.length; i++) {
  17. str += args[i];
  18. }
  19. return str;
  20. }
  21. }
  1. //判断如下两个方法是否构成重载:不是
  2. class Count{
  3. public static int getSum(int... nums){
  4. int sum = 0;
  5. for (int i = 0; i < nums.length; i++) {
  6. sum += nums[i];
  7. }
  8. return sum;
  9. }
  10. public static int getSum(int[] nums){
  11. int sum = 0;
  12. for (int i = 0; i < nums.length; i++) {
  13. sum += nums[i];
  14. }
  15. return sum;
  16. }
  17. }
  1. class Test06_Overload_Problem2{
  2. public static void main(String[] args){
  3. System.out.println(sum(1,2));//(int a, int b)
  4. System.out.println(sum(1,2,3));//(int... args)和(int a, int... args)都兼容,就有问题
  5. }
  6. //不调用编译没问题,但是调用时就有问题
  7. public static int sum(int a, int b){
  8. return a+b;
  9. }
  10. public static int sum(int... args){
  11. int sum = 0;
  12. for(int i=0; i<args.length; i++){
  13. sum += args[i];
  14. }
  15. return sum;
  16. }
  17. public static int sum(int a, int... args){
  18. int sum = a;
  19. for(int i=0; i<args.length; i++){
  20. sum += args[i];
  21. }
  22. return sum;
  23. }
  24. }

课后练习

1、声明一个数组工具类ArraysTools,包含几个重载方法

(1)重载方法系列1:可以为byte[],short[],int[],long[],double[],char[]数组实现从小到大排序

(2)重载方法系列2:可以遍历byte[],short[],int[],long[],double[],char[]数组,遍历结果形式:

  1. [元素1,元素2,。。。]

2、声明一个图形工具类GraphicTools,包含两个重载方法

(1)包含方法1:根据底边和高,求三角形面积,
(2)包含方法2:根据三条边,求三角形面积

提示:根据三角形三边求面积的海伦公式:

第5章 面向对象基础(上) - 图13

5.8 命令行参数(了解)

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

第5章 面向对象基础(上) - 图14

  1. public class TestCommandParam{
  2. //形参:String[] args
  3. public static void main(String[] args){
  4. System.out.println(args);
  5. System.out.println(args.length);
  6. for(int i=0; i<args.length; i++){
  7. System.out.println("第" + (i+1) + "个参数的值是:" + args[i]);
  8. }
  9. }
  10. }

运行命令:

  1. java TestCommandParam
  1. java TestCommandParam 1 2 3
  1. java TestCommandParam hello atguigu

5.9 static关键字

static是一个修饰符,可以修饰:

  • 成员变量,我们称为类变量,或静态变量,表示某个类的所有对象共享的数据
  • 成员方法,我们称为类方法,或静态方法,表示不需要实例对象就可以调用的方法,使用“类名.”进行调用

    • 父类的静态方法可以被继承不能被重写
    • 父接口的静态方法不能被实现类继承
  • 代码块,我们称为静态代码块,或静态初始化块,用于为静态变量初始化,每一个类的静态代码块只会执行一次,在类第一次初始化时执行
  • 成员内部类,我们称为静态成员内部类,简称静态内部类,不需要外部类实例对象就可以使用的内部类,在静态内部类中只能使用外部类的静态成员

    • static不能修饰top-level的类
  • 静态导入

  1. import static 包.类名.静态成员;
  2. import static 包.类名.*;

例如:使用一个枚举类的常量对象时,使用一个接口的内部接口时等

5.10 递归

  • 递归:指在当前方法内调用自己的这种现象。
  • 递归的分类:

    • 递归分为两种,直接递归和间接递归。
    • 直接递归称为方法自身调用自己。
    • 间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。
  • 注意事项

    • 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
    • 在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。

示例一:计算1-100之间所有自然数的和

  1. public class RecursionMethod1{
  2. public static void main(String[] args) {
  3. int sum = sum(100);
  4. System.out.println("1-100的和:" + sum);
  5. }
  6. public static int sum(int n){
  7. if(n == 1){
  8. return 1;
  9. }else{
  10. return n + sum(n-1);
  11. }
  12. }
  13. }

第5章 面向对象基础(上) - 图15

示例二:求n!

第5章 面向对象基础(上) - 图16

  1. public class RecursionMethod2{
  2. public static void main(String[] args) {
  3. int jieCheng = jieCheng(10);
  4. System.out.println("10的阶乘是:" + jieCheng);
  5. }
  6. public static int jieCheng(int n){
  7. if(n <= 1){
  8. return 1;
  9. }else{
  10. return n * jieCheng(n-1);
  11. }
  12. }
  13. }

第5章 面向对象基础(上) - 图17

示例三:计算斐波那契数列(Fibonacci)的第n个值

规律:一个数等于前两个数之和,

  1. f(0) =1
  2. f(1) = 1
  3. f(2) = f(0) + f(1) =2
  4. f(3) = f(1) + f(2) = 3,
  5. f(4) = f(2) + f(3) = 5
  6. ...
  7. f(n) = f(n-2) + f(n-1);
  1. public class RecursionMethod3{
  2. public static void main(String[] args) {
  3. Count c = new Count();
  4. System.out.println("f(10):" + c.f(10));
  5. System.out.println("f方法被调用的总次数:" + c.total);
  6. }
  7. }
  8. class Count{
  9. int total = 0;
  10. public int f(int n){
  11. total++;
  12. if(n <= 1){
  13. return 1;
  14. }else{
  15. return f(n-2) + f(n-1);
  16. }
  17. }
  18. }

第5章 面向对象基础(上) - 图18

练习

1、描述:猴子吃桃子问题,猴子第一天摘下若干个桃子,当即吃了所有桃子的一半,还不过瘾,又多吃了一个。第二天又将仅剩下的桃子吃掉了一半,又多吃了一个。以后每天都吃了前一天剩下的一半多一个。到第十天,只剩下一个桃子。试求第一天共摘了多少桃子?

第5章 面向对象基础(上) - 图19

2、有n级台阶,一次只能上1步或2步,共有多少种走法?

第5章 面向对象基础(上) - 图20

3、

第5章 面向对象基础(上) - 图21

5.11 对象数组

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

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

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

示例一:

(1)定义圆Circle类,包含radius半径属性,getArea()求面积方法,getPerimeter()求周长方法,String getInfo()返回圆对象的详细信息的方法

(2)在测试类中创建长度为5的Circle[]数组,用来装5个圆对象,并给5个圆对象的半径赋值为[1,10)的随机值

  1. class Test16_ObjectArray{
  2. public static void main(String[] args){
  3. //要在数组中存储5个圆对象
  4. //声明一个可以用来存储圆对象的数组
  5. Circle[] arr = new Circle[5];
  6. //for(int i=0; i<arr.length; i++){
  7. // System.out.println(arr[i]);
  8. //}
  9. //System.out.println(arr[0].radius);//NullPointerException
  10. //给元素赋值
  11. //元素的类型是:Circle,应该给它一个Circle的对象
  12. //arr[0] = 1.2;//错误的
  13. //arr[0]相当于它是一个Circle类型的变量,也是对象名,必须赋值为对象
  14. /*
  15. arr[0] = new Circle();
  16. arr[0].radius = 1.2;
  17. System.out.println(arr[0].radius);
  18. */
  19. //创建5个对象,半径随机赋值为[1,10)的随机值
  20. //Math.random()==>[0,1)
  21. //Math.random()*9==>[0,9)
  22. //Math.random()*9+1==>[1,10)
  23. for(int i=0; i<arr.length; i++){
  24. arr[i] = new Circle();//有对象才有半径
  25. arr[i].radius = Math.random()*9+1;
  26. }
  27. //遍历显示圆对象的信息
  28. for(int i=0; i<arr.length; i++){
  29. //arr[i]是一个Circle的对象,就可以调用Circle类中的属性和方法
  30. System.out.println(arr[i].getInfo());
  31. }
  32. }
  33. }
  34. class Circle{
  35. double radius;
  36. public double getArea(){
  37. return 3.14 * radius * radius;
  38. }
  39. public double getPerimeter(){
  40. return 3.14 * 2 * radius;
  41. }
  42. public String getInfo(){
  43. return "半径:" + radius +",面积:" + getArea() + ",周长:" + getPerimeter();
  44. }
  45. }

对象数组的内存图分析

练习1

(1)定义学生类Student

  1. 声明姓名和成绩实例变量,
  2. getInfo()方法:用于返回学生对象的信息

(2)测试类ObjectArrayTest的main中创建一个可以装3个学生对象的数组,并且按照学生成绩排序,显示学生信息

  1. public class ObjectArrayTest {
  2. public static void main(String[] args) {
  3. Student[] arr = new Student[3];
  4. arr[0] = new Student();
  5. arr[0].name = "张三";
  6. arr[0].score = 89;
  7. arr[1] = new Student();
  8. arr[1].name = "李四";
  9. arr[1].score = 84;
  10. arr[2] = new Student();
  11. arr[2].name = "王五";
  12. arr[2].score = 85;
  13. for (int i = 1; i < arr.length; i++) {
  14. for (int j = 0; j < arr.length-1; j++) {
  15. if(arr[j].score > arr[j+1].score){
  16. Student temp = arr[j];
  17. arr[j] = arr[j+1];
  18. arr[j+1] = temp;
  19. }
  20. }
  21. }
  22. for (int i = 0; i < arr.length; i++) {
  23. System.out.println(arr[i].getInfo());
  24. }
  25. }
  26. }
  27. class Student{
  28. String name;
  29. int score;
  30. public String getInfo(){
  31. return "姓名:" + name + ",成绩:" + score;
  32. }
  33. }
  1. class Test18_ObjectArrayExer2_2{
  2. public static void main(String[] args){
  3. //创建一个可以装3个学生对象的数组
  4. Student[] arr = new Student[3];//只是申明这个数组,可以用来装3个学生,此时里面没有学生对象
  5. //从键盘输入
  6. java.util.Scanner input = new java.util.Scanner(System.in);
  7. for(int i=0;i<arr.length; i++){
  8. System.out.println("请输入第" + (i+1) + "个学生信息:");
  9. arr[i] = new Student();
  10. System.out.print("姓名:");
  11. arr[i].name = input.next();
  12. System.out.print("成绩:");
  13. arr[i].score = input.nextInt();
  14. }
  15. //先显示一下目前的顺序
  16. for(int i=0; i<arr.length; i++){
  17. System.out.println(arr[i].getInfo());
  18. }
  19. System.out.println("------------------------------------------");
  20. //冒泡排序
  21. for(int i=1; i<arr.length; i++){
  22. for(int j=0; j<arr.length-i; j++){
  23. //arr[j] > arr[j+1]//错误的
  24. if(arr[j].score > arr[j+1].score){
  25. //交换两个元素,这里是两个学生对象,所以temp也得是Student类型
  26. Student temp = arr[j];
  27. arr[j] = arr[j+1];
  28. arr[j+1] = temp;
  29. }
  30. }
  31. }
  32. //再显示一下目前的顺序
  33. for(int i=0; i<arr.length; i++){
  34. System.out.println(arr[i].getInfo());
  35. }
  36. }
  37. }
  38. class Student{
  39. String name;
  40. int score;//使用int或double都可以
  41. public String getInfo(){
  42. return "姓名:" + name +",成绩:" + score;
  43. }
  44. }