Java全栈(二)JavaSE:12.面向对象上

1.什么是面向对象

(1)面向对象与面向过程

在这里插入图片描述

  • 面向对象:简单的可以理解为将复杂的事情分解为许多简单的事情。再将这些简单的事情的解决办法封装到类对象中。就像我们造汽车一样,并不会一步一步的去造,而是将汽车分解为许多零部件,如何将这些零部件组装起来就行了。
  • 面向对象是一种框架,一种思想。当我们具体实现每一个小功能时,仍然是使用面向过程的思想。

(2)什么是面向对象

在这里插入图片描述

  • 面向对象的核心就是类和对象。
  • 类:就是一类事务的抽象,它有该类事务的属性和方法。例如:人我们可以抽象为一个类,它们都能移动,都能叫,都需要吃饭等等。
  • 对象:对象是类的具体表现,类是对象的模板。对象都是通过类来产生的。例如:人是一个类,张三就是一个具体的对象。

2.类和对象

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

什么是类

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

  • 类可以看做是一个模版,或者图纸,系统根据类的定义来造出对象。我们要造一个汽车,怎么样造?类就是这个图纸,规定了汽车的详细信息,然后根据图纸将汽车造出来。

    类:我们叫做class。 对象:我们叫做Object,instance(实例)。以后我们说某个类的对象,某个类的实例。是一样的意思。

    示例1:

    英雄联盟、王者荣耀中的类和对象

在这里插入图片描述

英雄就是类,具体的英雄,盖伦、提莫是对象。

示例2:

月饼模具和月饼

月饼模具是类,使用月饼模具制作的一个个月饼就是对象

在这里插入图片描述

现实中,描述一类事物:

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

举例:小猫。

​ 属性:名字、体重、年龄、颜色。
​ 行为:走、跑、叫。

什么是对象

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

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

举例:一只小猫。

​ 属性:tom、5kg、2 years、yellow。
​ 行为:溜墙根走、蹦跶的跑、喵喵叫。

类与对象的关系

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

在这里插入图片描述

3.类的定义和对象的创建

事物与类的对比

现实世界的一类事物:

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

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

成员变量:对应事物的属性
成员方法:对应事物的行为

类的定义格式

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

类的定义格式举例:

public class Person {
  	//成员变量
  	String name;//姓名
    int age;//年龄
    boolean isMarried;
    
    public void walk(){
        System.out.println("人走路...");
    }
    public String display(){
        return "名字是:" + name + ",年龄是:" + age + ",Married:" + isMarried;
    }
}

对象的创建

创建对象:

new 类名()//也称为匿名对象

//给创建的对象命名
//或者说,把创建的对象用一个引用数据类型的变量保存起来
类名 对象名 = new 类名();

类似于:

System.out.println("高老师年龄是:" + 18);//如果确定只在这里一次性使用,那么可以不用变量保存(#^.^#)

//把18用int类型的age变量保存起来,方便后面使用
int age = 18;
System.out.println("高老师年龄是:" + age);
System.out.println("仓老师比高老师大10岁,年龄是:" + (age+10));

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

class Student{
    
}
public class TestStudent{
    //Java程序的入口
    public static void main(String[] args){
        System.out.println(new Student());//Student@7852e922

        Student stu = new Student();
        System.out.println(stu);//Student@4e25154f
        
        int[] arr = new int[5];
		System.out.println(arr);//[I@70dea4e
    }
}
//Student和TestStudent没有位置要求,谁在上面谁在下面都可以
//但是如果TestStudent类的main中使用了Student类,那么要求编译时,这个Student已经写好了,不写是不行的
//如果两个类都在一个.java源文件中,只能有一个类是public的

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

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

在这里插入图片描述

创建对象内存图

在这里插入图片描述
注意:每创建一个对象,就会分配一块独立的空间。用于储存相关成员变量的信息。

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、声明包的语法格式

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语句的语法格式:

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

注意:

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

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

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

示例代码:

package com.atguigu.bean;

public class Student {
	// 成员变量
	private String name;
	private int age;

	// 构造方法
	public Student() {
	}

	public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}

	// 成员方法
	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getAge() {
		return age;
	}
}
package com.atguigu.test;

import java.util.Scanner;
import java.util.Date;
import com.atguigu.bean.Student;

public class Test{
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        Student stu = new Student();
        String str = "hello";
        
        Date now = new Date();
        java.sql.Date d = new java.sql.Date(346724566);        
    }
}

5. 成员变量

1、成员变量的分类

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

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

2、如何声明成员变量?

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

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

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

class Chinese{
	static String country;
	String name;
    char gender = '男';//显式赋值
}

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

(1)类变量
类名.静态成员变量  //推荐

对象名.静态成员变量 //不推荐
(2)实例变量
对象名.非静态成员变量  //只能使用这种方式

例如:

public class TestChinese {
	public static void main(String[] args) {
		//类名.静态成员变量
		System.out.println(Chinese.country);
		//错误,非静态成员变量必须通过对象.进行访问
//		System.out.println(Chinese.name);
		
		Chinese c1 = new Chinese();
		//对象名.非静态成员变量
		System.out.println(c1.name);
		//静态的成员变量也可以通过对象.进行访问
		//对象名.非静态成员变量
		System.out.println(c1.country);
        System.out.println(c1.gender);
	}
}
class Chinese{
	static String country;
	String name;
    char gender = '男';
}

4、成员变量的特点

(1)成员变量有默认值
基本类型整数(byte,short,int,long)0
浮点数(float,double)0.0
字符(char)‘\u0000’
布尔(boolean)false
数据类型默认值
引用类型数组,类,接口null
(2)类变量的值是所有对象共享的,而实例变量的值是每个对象独立的
public class TestChinese {
	public static void main(String[] args) {
		Chinese c1 = new Chinese();
		Chinese c2 = new Chinese();
		
		c1.name = "张三";
		c2.name = "李四";
        c2.gender = '女';
		
//		c1.country = "中国";
		Chinese.country = "中国";//推荐
		
		System.out.println("c1.country = " + c1.country + ",c1.name = " + c1.name + ",c1.gender = " + c1.gender);
		System.out.println("c2.country = " + c2.country + ",c2.name = " + c2.name + ",c2.gender = " + c2.gender);
	}	
}
class Chinese{
	static String country;
	String name;
    char gender = '男';
}

5、成员变量的内存图

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

在这里插入图片描述

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

class Chinese{
	static String country = "中国";//静态变量,所有中国人的国家的名称是一样,只需要存储一份
	String name;//实例变量,每一个中国人的姓名是独立,每一个对象单独存储
}

运行结果如下:

中国
张三
中国
李四
--------------------------------------
中华人民共和国
中华人民共和国
中华人民共和国
张三丰
李四

在这里插入图片描述

class MyDate{
	int year;
	int month;
	int day;
}
class Employee{
	String name;
	MyDate birthday;
}
class Test09FieldExer3{
	public static void main(String[] args){
		//创建两个员工对象
		Employee e1 = new Employee();
		Employee e2 = new Employee();
		
		//为两个员工对象的成员变量赋值
		e1.name = "张三";
		e1.birthday = new MyDate();
		
		e2.name = "李四";
		e2.birthday = new MyDate();
		
		e1.birthday.year = 2000;
		e1.birthday.month = 1;
		e1.birthday.day = 1;
		
		e2.birthday.year = 2000;
		e2.birthday.month = 3;
		e2.birthday.day = 8;
		
		System.out.println("第一个员工,姓名:" + e1.name +",生日:" + e1.birthday.year + "年" + e1.birthday.month + "月" + e1.birthday.day + "日");
		System.out.println("第二个员工,姓名:" + e2.name +",生日:" + e2.birthday.year + "年" + e2.birthday.month + "月" + e2.birthday.day + "日");
	}
}

运行结果如下:

第一个员工,姓名:张三,生日:2000年1月1日
第二个员工,姓名:李四,生日:2000年3月8日

在这里插入图片描述

实例变量(非静态变量内存图)

在这里插入图片描述

类变量(静态变量内存图)

  • 问题:在上面的内存图分析中,我们发现每一次创建对象都会给变量country开辟空间,但每一次赋值都是一样的。我们是否可以将country的空间实现复用呢
  • 解决:将country设置为类变量
    在这里插入图片描述

6、类变量与实例变量的区别

  • 声明方式不同:类变量需要加static修饰,实例变量不需要
  • 调用方式不同:类变量使用类名去调用,实例变量只能使用实例对象去调用
  • 内存不同:类变量存放在方法区中,所有对象共享一份。实例变量存放在堆中,每一个对象一份
  • 初始化时间不同:类变量随着类的加载完成初始化;实例变量随着实例对象的创建完成初始化。即类变量早于实例变量完成初始化
  • 销毁时间不同:类变量随着类的销毁而销毁;实例变量当没有引用指向时,会被垃圾回收器(GC)回收销毁。即类变量的生命周期比实例变量更长

7.成员变量练习

练习1
需求:

  • 声明一个银行账户类,有属性:利率、账号、余额
  • 在测试类的main中,创建账户类的两个对象,其中所有账户的利率是相同的,都是0.035,而账号和余额是不同的,并打印显示

银行账户类代码:

package com.oy.面向对象.opp1;

public class Account {
    // 账号属性
    String account;
    // 利率属性
    static double rate = 0.035;
    // 余额属性
    double balance;
}

测试类代码如下:

package com.oy.面向对象.opp1;

public class Test {
    public static void main(String[] args) {
        //声明第一个账户
        Account ac1 = new Account();
        ac1.account = "adasfvdsgffhn";
        ac1.balance = 259632.258;
        System.out.println("账户为:"+ ac1.account+"; 利率为:"+Account.rate+"; 余额为:"+ac1.balance);
        //声明第二个账户
        Account ac2 = new Account();
        ac2.account = "asfdscbvbghgggg";
        ac2.balance = 3005.25;
        System.out.println("账户为:"+ ac2.account+"; 利率为:"+Account.rate+"; 余额为:"+ac2.balance);
    }
}

运行测试类结果如下:

账户为:adasfvdsgffhn; 利率为:0.035; 余额为:259632.258
账户为:asfdscbvbghgggg; 利率为:0.035; 余额为:3005.25

练习2
需求:

  • 声明一个MyDate类型,有属性:年,月,日

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

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

代码如下:

package com.oy.面向对象;

public class Demo3 {
    public static void main(String[] args) {
        // 声明第一个员工对象
        Employee employee1 = new Employee();
        employee1.name = "张三";
        //注意:员工的生日属性应该是一个MyDate对象
        employee1.birthday = new MyDate();
        employee1.birthday.year = 1994;
        employee1.birthday.month = 1;
        employee1.birthday.day = 29;
        System.out.println("员工姓名为:"+employee1.name+"; 员工生日为:"+
                employee1.birthday.year+"-"
                +employee1.birthday.month+"-"
                +employee1.birthday.day);

        // 声明第二个员工对象
        Employee employee2 = new Employee();
        employee2.name = "李四";
        //注意:员工的生日属性应该是一个MyDate对象
        employee2.birthday = new MyDate();
        employee2.birthday.year = 1992;
        employee2.birthday.month = 12;
        employee2.birthday.day = 1;
        System.out.println("员工姓名为:"+employee2.name+"; 员工生日为:"+
                employee2.birthday.year+"-"
                +employee2.birthday.month+"-"
                +employee2.birthday.day);
    }
}

class MyDate {
    public int year;
    public int month;
    public int day;
}

class Employee{
    public String name;
    public MyDate birthday;
}

运行效果如下:

员工姓名为:张三; 员工生日为:1994-1-29
员工姓名为:李四; 员工生日为:1992-12-1

6. 成员方法

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

1.方法的概念

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

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

2 .方法的原则

方法的使用原则:

(1)必须先声明后使用

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

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

3.成员方法的分类

成员方法分为两类:

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

4.如何声明方法

1、方法声明的位置必须在类中方法外,不能在方法中声明方法。

2、语法格式

【修饰符】 返回值类型 方法名(【参数列表:参数类型1 参数名1,参数类型2 参数名, ......){
        方法体;
        【return 返回值;}
  • 修饰符: 修饰符后面一一介绍,例如:public,static等都是修饰符
  • 返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者
    • 基本数据类型
    • 引用数据类型
    • 无返回值类型:void
  • 方法名:给方法起一个名字,见名知意,能准确代表该方法功能的名字
  • 参数列表:方法内部需要用到其他方法中的数据,需要通过参数传递的形式将数据传递过来,可以是基本数据类型、引用数据类型、也可以没有参数,什么都不写
  • 方法体:特定功能代码
  • return:结束方法,并将方法的结果返回去,
    • 如果返回值类型不是void,方法体中必须保证一定有return 返回值;语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容。如果是兼容的数据类型,必须在声明数据类型范围内。例如声明double类型,返回一个int类型也是ok的。
    • 如果返回值类型为void时,return 后面不用跟返回值,甚至也可以没有return语句。
    • return语句后面就不能再写其他代码了,否则会报错:Unreachable code

声明位置示例:

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

错误示例:

{
    方法1(){
        方法2(){  //位置错误
        
   		}
    }
}
示例一:

声明一个圆的图形类:

​ 属性(成员变量):半径,

​ 成员方法:求面积的方法,返回圆对象信息的方法

class Circle{
	double radius;
	double area() {
		return Math.PI * radius * radius;
	}
}

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

示例二:

声明一个计算工具类CountTools:

​ 方法1:求两个整数的最大值

class CountTools{
	static int max(int a, int b) {
        return a > b ? a : b;
	}
}

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

5.如何在其他类中调用方法

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

示例代码:

public class TestCircle {
	public static void main(String[] args) {
		Circle c1 = new Circle();
		c1.radius = 1.2;
		System.out.println("c1的面积:" + c1.area());
		//非静态方法只能通过"对象."进行访问
//		System.out.println("c1的面积:" + Circle.area());
        
		Circle c2 = new Circle();
		c2.radius = 2.5;
		System.out.println("c2的面积:" + c2.area());
	}
}
class Circle{
	double radius;
	public double area() {
		return Math.PI * radius * radius;
	}
}
(2)类方法
类名.类方法(【实参列表】)  //推荐

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

示例:

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

7.方法的形参与实参

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

总结:

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

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

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

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

package com.oy.面向对象.成员方法;

public class Demo1 {

    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        // 传入的实参:x,y必须是int类型。且x的值会赋给max方法中的形参a,y的值会赋给max方法中的形参b
        int c =  new Demo1().max(x,y);
        System.out.println(c);
    }


    public int max(int a,int b){
        if (a>b){
            return a;
        }
        else {
            return b;
        }
    }
}

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

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

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

class Circle{
	double radius;
	
	//写一个方法,可以返回“圆对象”的详细信息
	String getDetailInfo(){
		return "半径:" + radius + ",面积:" + area() +",周长:" + perimeter();
	}
	
	//写一个方法,可以返回“圆对象”的面积
	double area(){
		return Math.PI*radius*radius;
	}
	
	//写一个方法,可以返回“圆对象”的周长
	double perimeter(){
		return 2*Math.PI*radius;
	}

}
class Test{
		
	static void test(){
		System.out.println("");
	}
	void method(){
		 test();
	}
    
    public static void main(String[] args){
        method();//错误
        test();//正确
    }
}

7.方法调用内存分析

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

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

示例一:

public class TestCount {
	public static void main(String[] args) {
        int a = 4;
        int b = 2;
		int m = CountTools.max(a, b));
	}
}
class CountTools{
	static int max(int a, int b) {
		return a > b ? a : b;
	}
}

在这里插入图片描述

示例二:

public class TestCircle {
	public static void main(String[] args) {
		Circle c1 = new Circle();
		c1.radius = 1.2;
		int area1 = c1.area();
		
		Circle c2 = new Circle();
		c2.radius = 2.5;
		int area2 = c2.area();
	}
}
class Circle{
	double radius;
	public double area() {
		return Math.PI * radius * radius;
	}
}

在这里插入图片描述

示例三:

public class Test {
	public static void main(String[] args) {
		int[] arr = {2,4,1,5,3};
		
		ArrayUtil.sort(arr);
		
		for (int i = 0; i < arr.length; i++) {
			System.out.println(arr[i]);
		}
	}
}
class ArrayUtil{
	public static void sort(int[] arr){
		for (int i = 1; i < arr.length; i++) {
			for (int j = 0; j < arr.length - i; j++) {
				if(arr[j] > arr[j+1]){
					int temp = arr[j];
					arr[j] = arr[j+1];
					arr[j+1] = temp;
				}
			}
		}
	}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rmSE6E50-1630499561954)(imgs/1572350909017.png)]

8.静态方法与非静态方法的区别

  • 静态资源需要static修饰,而非静态资源不需要
  • 静态资源随着类的加载而加载,而非静态资源必须随着对象的创建而加载
  • 非静态资源中可以直接调用本类中的所有成员(包括类变量、实例变量、静态方法和非静态方法)。而静态方法中只能直接调用本类中的类变量和静态方法,不能直接调用本类中的实例变量和非静态方法。这是因为静态方法随着类的加载而加载,非静态方法随着对象的加载而加载。所以存在当静态方法已经加载而对象还未创建的情况,这时的实例变量和非静态方法还没有分配空间。

9.方法的参数传递机制

  • 方法的参数传递机制:实参给形参赋值
    • 方法的形参是基本数据类型时,形参值的改变不会影响实参;
    • 方法的形参是引用数据类型时,形参地址值的改变不会影响实参,但是形参地址值里面的数据的改变会影响实参,例如,修改数组元素的值,或修改对象的属性值。
      • 注意:String、Integer等特殊类型容易错

实例:我们写一个练习来分析
代码如下:

package com.oy.面向对象.成员方法;

public class Demo2 {
    public static void main(String[] args) {
        int num1 = 66;
        int num2 = 88;
        System.out.println("changeValue()调用前:num1:"+num1+",num2:"+num2);
        changeValue(num1,num2);
        System.out.println("changeValue()调用后:num1:"+num1+",num2:"+num2);
    }

    public static void changeValue(int num1,int num2){
        int temp = num1;
        num1 = num2;
        num2 = temp;
        System.out.println("changeValue()调用中:num1:"+num1+",num2:"+num2);

    }

}

运行结果如下:

changeValue()调用前:num1:66,num2:88
changeValue()调用中:num1:88,num2:66
changeValue()调用后:num1:66,num2:88

画内存图来分析:

  • 在mian方法和changeValue方法中的num1和num2并不是在同一块内存空间下,它们都属于局部变量,只在对应的空间下有效。
  • 所以在mian方法中调用changeValue方法时,只是把main方法中的变量num1和num2的值传递给changeValue方法中的num1和num2。changeValue方法中的num1和num2值的改变并不会影响到main方法中num1和num2的值
    在这里插入图片描述

示例代码1:传递基本数据类型

class Test{
    public static void swap(int a, int b){
        int temp = a;
        a = b;
        b = temp;
	}

	public static void main(String[] args){
        int x = 1;
        int y = 2;
        swap(x,y);//调用完之后,x与y的值不变
    }
}

示例代码2:传递引用对象类型

public class Demo2 {
    public static void main(String[] args) {
        Point point = new Point();
        point.x = 66;
        point.y = 88;
        System.out.println("changeValue()调用前:point.x:"+ point.x+",point.y:"+ point.y);
        changeValue(point);
        System.out.println("changeValue()调用后:point.x:"+ point.x+",point.y:"+ point.y);
    }

    public static void changeValue(Point point){
        int temp = point.x;
        point.x = point.y;
        point.y = temp;
        System.out.println("changeValue()调用中:point.x:"+ point.x+",point.y:"+ point.y);

    }

}
class Point{
    public int x;
    public int y;
}

运行结果如下:

changeValue()调用前:point.x:66,point.y:88
changeValue()调用中:point.x:88,point.y:66
changeValue()调用后:point.x:88,point.y:66

画内存图分析:因为调用changeValue方法时传递的是一个引用数据类型,它的值是该对象在堆中的内存地址。所以在main方法和changeValue方法中,point虽然不是同一个变量但它们的值都是指向同一个堆中的内存空间。所以改变changeValue方法中point值的属性值,main方法中的point值的属性值也会被改变。在这里插入图片描述

示例代码3:传递数组对象

package com.oy.面向对象.成员方法;

public class Demo3 {
    public static void main(String[] args) {
        String[] arr = {"范冰冰","蔡徐坤"};
        System.out.println("changeValue()调用前:arr[0]:"+ arr[0]);
        changeValue(arr);
        System.out.println("changeValue()调用后:arr[0]:"+ arr[0]);
    }

    public static void changeValue(String[] arr){
        arr[0] = "韩红";
        arr = new String[]{"谢广坤","赵四"};
        System.out.println("changeValue()调用中:arr[0]:"+ arr[0]);
    }
}

运行结果如下:

changeValue()调用前:arr[0]:范冰冰
changeValue()调用中:arr[0]:谢广坤
changeValue()调用后:arr[0]:韩红

画内存图分析:
在这里插入图片描述

陷阱1:

/*
陷阱1:在方法中,形参 = 新new对象,那么就和实参无关了
*/
class Test{
    public static void change(MyData my){
        my = new MyData();//形参指向了新对象
        my.num *= 2;
    }
    
    public static void main(String[] args){
        MyData m = new MyData();
        m.num = 1;
        
        change(m);//调用完之后,m对象的num属性值仍然为1
    }
}

class MyData{
    int num;
}

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

public class Test {
	public static void main(String[] args) {
		StringUtil util = new StringUtil();
		String str = "尚硅谷";
		util.change(str);
		System.out.println(str);
	}
}
class StringUtil{
	public void change(String str){
		str += "你好";//String对象不可变,一旦修改就会产生新对象
	}
}

7.成员变量与局部变量的区别

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)局部变量:没有,必须初始化
其中的形参比较特殊,靠实参给它初始化。

8.可变形参

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

格式:

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

要求:

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

(2)如果一个方法包含可变参数,那么可变参数必须是形参列表的最后一个
(3)可变参数在方法中是作为一个数组定义的

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

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

示例一: 求n个整数的和
public class ChangeArgs {
	public static void main(String[] args) {
		int[] arr = { 1, 4, 62, 431, 2 };
		int sum1 = getSum1(arr);
		System.out.println(sum1);

		int sum2 = getSum2(arr);
		System.out.println(sum2);
		int sum3 = getSum2(1, 4, 62, 431, 2);
		System.out.println(sum3);
	}

	// 完成数组 所有元素的求和
	// 原始写法
	public static int getSum1(int[] arr) {
		int sum = 0;
		for (int i = 0; i < arr.length; i++) {
			sum += arr[i];
		}

		return sum;
	}

	// 可变参数写法
	public static int getSum2(int... arr) {
		int sum = 0;
		for (int i = 0; i < arr.length; i++) {
			sum += arr[i];
		}
		return sum;
	}
}
示例二:求1-n个整数中的最大值
public class ChangeArgs_Exer1 {
	public static void main(String[] args) {
		System.out.println(max(1));
		System.out.println(max(5,3,2,6));
	}

	public static int max(int num, int... others){
		int max = num;
		for (int i = 0; i < others.length; i++) {
			if(max < others[i]){
				max = num;
			}
		}
		return max;
	}
}
示例三:字符串拼接

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

public class ChangeArgs_Exer2 {
	public static void main(String[] args) {
		System.out.println(concat());
		System.out.println(concat("hello","world"));
	}
	public static String concat(String... args){
		String str = "";
		for (int i = 0; i < args.length; i++) {
			str += args[i];
		}
		return str;
	}
}

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

public class ChangeArgs_Exer4 {
	public static void main(String[] args) {
		System.out.println(concat('+'));
		System.out.println(concat('+',"hello","world"));
	}
	public static String concat(char seperator, String... args){
		String str = "";
		for (int i = 0; i < args.length; i++) {
			if(i==0){
				str += args[i];
			}else{
				str += seperator + args[i];
			}
		}
		return str;
	}
}
课后练习

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

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

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

​ 例如:字符串.length(), “hello”.length()返回5

​ 获取字符串[index]位置的字符:char charAt(int index)

​ 例如:字符串.charAt(index) hello.charAt(1)返回’e’

参考答案:

class Test12MethodExer1{
	public static void main(String[] args){
		System.out.println(maxYue(6,9));
		System.out.println(maxYue(16,18,4,8));
	}
	
	public static int maxYue(int... args){
		//找很多个数的公约数
		//(1)找出它们中最小的
		//类似于在数组中找最小值
		int min = args[0];
		for(int i=1; i<args.length; i++){
			if(args[i] < min){
				min = args[i];
			}
		}
		
		//(2)从小的数往1的方向找,找到的第一个公约数就是它们的最大公约数
		for(int i=min; i>=1; i--){
			//这个i得把args中所有的数都整除了,那么i就是他们的公约数
			boolean flag = true;//假设i可以把args中所有数都整除了
			for(int j=0; j<args.length; j++){
				if(args[j] % i !=0){//args中有一个数不能被i整除,说明这个i不是它们的公约数
					flag = false;
					break;
				}
			}
			if(flag){
				return i;//return会结束当前方法
			}
		}
		
		return 1;//1是所有数的公约数
		/*
		假设args中6和9  args[0]是6,args[1]是9,min=6
		外循环第一次i=min=6,  
				内循环第一次:j=0,  if(args[0] % 6!=0)不成立 j++
				内循环第二次:j=1,  if(args[1] % 6!=0)成立   flag = false  ;break;
				说明i不是它们公约数
		外循环第二次i=5
				内循环第一次:j=0,  if(args[0] % 5 !=0)成立 j++  flag = false;break;
				说明i不是它们公约数
		外循环第三次i=4
				内循环第一次:j=0,  if(args[0] % 4 !=0)成立 j++  flag = false;break;
				说明i不是它们公约数
		外循环第四次i=3
				内循环第一次:j=0,  if(args[0] % 3 !=0)不成立 j++ 
				内循环第二次:j=1,  if(args[1] % 3 !=0)不成立 j++  
				if(flag)成立,return i;
		*/
	}
}
public static void main(String[] args) {
        // 声明一个方法,可以找出任意个整数的最大公约数
        int maxYueShu = getMaxYueShu(10, 20, 15, 20);
        System.out.println("maxYueShu = " + maxYueShu);
        System.out.println(sameChars("hello", "world"));//ol
        System.out.println(sameChars("chai", "wolrd"));
        System.out.println(sameChars("atguigu", "java"));
        System.out.println(sameChars("samewordsg", "string", "words"));
    }

    public static String sameChars(String... words) {
        String same = "";
        for (char a = 'A'; a <= 'z'; a++) {
            //统计相同单词的个数
            int count = 0;
            for (int j = 0; j < words.length; j++) {
                for (int h = 0; h < words[j].length(); h++) {
                    if (words[j].charAt(h) == a) {
                        count++;
                        break;
                    }
                }
            }
            //当相同的个数等于字符个数时 判定所有的字符串都出现过
            if (count == words.length) {
                same += a;
            }
        }
        return same;
    }
可变形参注意点
  • 注意点1:在类中同时声明方法:public static void max(int... arr){}public static void max(int[] arr){}会编译报错。因为可变参数int...其底层就是通过数组实现的,所以程序会认为这两个方法一样,导致报错。
  • 注意点2:在类中同时声明方法:public static void max(int... arr){}public static void max(int a,int b,int c){},编译时不会报错,但调用方法时可能会报错,因为程序有可能不知道你是想要调用哪个方法。

9.方法重载

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

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

public class Method_Demo6 {
    public static void main(String[] args) {
        //定义不同数据类型的变量
        byte a = 10;
        byte b = 20;
        short c = 10;
        short d = 20;
        int e = 10;
        int f = 10;
        long g = 10;
        long h = 20;
        // 调用
        System.out.println(compare(a, b));
        System.out.println(compare(c, d));
        System.out.println(compare(e, f));
        System.out.println(compare(g, h));
    }
    // 两个byte类型的
    public static boolean compare(byte a, byte b) {
        System.out.println("byte");
        return a == b;
    }

    // 两个short类型的
    public static boolean compare(short a, short b) {
        System.out.println("short");
        return a == b;
    }

    // 两个int类型的
    public static boolean compare(int a, int b) {
        System.out.println("int");
        return a == b;
    }

    // 两个long类型的
    public static boolean compare(long a, long b) {
        System.out.println("long");
        return a == b;
    }
}

10.命令行参数(了解)

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

在这里插入图片描述

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

运行命令:

java TestCommandParam
java TestCommandParam 1 2 3
java TestCommandParam hello atguigu

11.static关键字

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

  • 成员变量,我们称为类变量,或静态变量,表示某个类的所有对象共享的数据
  • 成员方法,我们称为类方法,或静态方法,表示不需要实例对象就可以调用的方法,使用“类名."进行调用
    • 父类的静态方法可以被继承不能被重写
    • 父接口的静态方法不能被实现类继承
  • 代码块,我们称为静态代码块,或静态初始化块,用于为静态变量初始化,每一个类的静态代码块只会执行一次,在类第一次初始化时执行
  • 成员内部类,我们称为静态成员内部类,简称静态内部类,不需要外部类实例对象就可以使用的内部类,在静态内部类中只能使用外部类的静态成员
    • static不能修饰top-level的类
  • 静态导入
import static.类名.静态成员;
import static.类名.*;

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

12.递归

  • 递归:指在当前方法内调用自己的这种现象。
  • 递归的分类:
    • 递归分为两种,直接递归和间接递归。
    • 直接递归称为方法自身调用自己。
    • 间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。
  • 注意事项
    • 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
    • 在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。
示例一:计算1-100之间所有自然数的和
public class RecursionMethod1{
	public static void main(String[] args) {
		int sum = sum(100);
		System.out.println("1-100的和:" + sum);
	}

	public static int sum(int n){
		if(n == 1){
			return 1;
		}else{
			return n + sum(n-1);
		}
	}
}

在这里插入图片描述

示例二:求n!

在这里插入图片描述

public class RecursionMethod2{
	public static void main(String[] args) {
		int jieCheng = jieCheng(10);
		System.out.println("10的阶乘是:" + jieCheng);
	}
	public static int jieCheng(int n){
		if(n <= 1){
			return 1;
		}else{
			return n * jieCheng(n-1);
		}
	}
}

在这里插入图片描述

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

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

​ f(0) =1,

​ f(1) = 1,

​ f(2) = f(0) + f(1) =2,

​ f(3) = f(1) + f(2) = 3,

​ f(4) = f(2) + f(3) = 5

​ …

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

public class RecursionMethod3{
	public static void main(String[] args) {
		Count c = new Count();
		
		System.out.println("f(10):" + c.f(10));
		System.out.println("f方法被调用的总次数:" + c.total);
	}
}
class Count{
	int total = 0;
	public int f(int n){
		total++;
		if(n <= 1){
			return 1;
		}else{
			return f(n-2) + f(n-1);
		}
	}
}

在这里插入图片描述

练习

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

package com.oy.面向对象.成员方法;

/*
需求:猴子吃桃子问题,猴子第一天摘下若干个桃子,当即吃了所有桃子的一半,还不过瘾,又多吃了一个。第二天又将仅剩下的桃子吃掉了一半,又多吃了一个。
以后每天都吃了前一天剩下的一半多一个。到第十天,只剩下一个桃子。试求第一天共摘了多少桃子?
思路:
第n天剩余桃子:      m
第n+1天剩余桃子:    m-(m/2+1)=m/2 -1
即:day(n) = (day(n+1)+1)*2
* */

public class Demo5 {
    public static void main(String[] args) {
        int res = test(1);
        System.out.println(res);
    }


    /**
     * 猴子吃桃子问题
     * @return
     */
    public static int test(int day){
        if (day == 10){
            return 1;
        }
        else {

            return (test(day+1)+1)*2;

        }
    }
}

13.对象数组

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

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

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

示例一:

package com.oy.面向对象.成员方法;

public class Demo6 {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "李白";
        p1.age = 18;
        Person p2 = new Person();
        p2.name = "杜甫";
        p2.age = 19;
        Person[] perArr = {p1,p2};
        for(Person p:perArr){
            p.show();
        }
    }
}

class Person{
    String name;
    int age;
    public void show(){
        System.out.println("名字是:"+name+",年龄是:"+age);
    }
}

运行结果如下:

名字是:李白,年龄是:18
名字是:杜甫,年龄是:19

对象数组的内存图分析

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.编写MyDate.java 2.该有如下构造方法 2.1 无参数构造方法public MyDate(),以当前的系统时间构造MyDate对象 2.2 public MyDate(int year, int month, int day), 以指定的年月日构造MyDate对象 3.该有如下属性 3.1 private int year ;//年 3.2 private int month; //月 3.3 private int day; //日 4.该有如下方法 4.1 public String after(int day); //返回当前对象代表的日期之后day天的日期,比如当前对象是2008-8-8,调用after(5)以后,应该返回2008-8-13,格式可自定义 4.2 public String before(int day); //返回当前对象代表的日期之前day天的日期,比如当前对象是2008-8-8,调用before(5)以后,应该返回2008-8-3,格式可自定义 4.3 public void setYear(int year); //设置年为指定值year 4.4 public void setMonth(int month); //设置月为指定值month 4.5 public void setDay(int day); //设置日为指定值day 4.6 public int getYear(); //返回当前对象的年 4.7 public int getMonth(); //返回当前对象的月 4.8 public int getDay(); //返回当前对象的日 4.9 public void set (int year, int month, int day); //设置年、月、日为指定的值year、month、day 4.10 public String toString();//以字符串形式返回当前对象的年月日,例如2008年08月08日,格式可自定义 4.11 public boolean equals(MyDate mydate);//当前对象与另一个对象比较,如果两个对象的年月日均相等,则返回true,反之返回false 5.编写TestMyDate.java,在main方法中对MyDate的各个方法进行测试 6.按照编程规范为MyDate.java编写规范的代码 7.按照java doc API的要求,对代码编写规范的注释,然后利用javadoc.exe命令生成MyDate.java的API doc 8.撰写上机报告

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值