java--面向对象基础(一)

面向对象基础(一)

1 面向对象思想概述

1、概述

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

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

面向过程:POP: Process-Oriented Programming

​ 以函数(方法)为最小单位

​ 数据独立于函数之外

​ 以过程,步骤为主,考虑怎么做

面向对象:OOP: Object Oriented Programming

​ 以类/对象为最小单位,类包括:数据+方法

​ 以对象(谁)为主,考虑谁来做,谁能做

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

程序员的角色:

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

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

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

3、面向对象的基本特征

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

2 类和对象

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

什么是类

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

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

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

什么是对象

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

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

举例:一只小猫。

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

类与对象的关系

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

在这里插入图片描述

3 类的定义和对象的创建

事物与类的对比

现实世界的一类事物:

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

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

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

类的定义格式

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

对象的创建

创建对象:

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

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

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

类、数组都是引用数据类型,引用数据类型的变量中存储的是对象的地址,或者说指向堆中对象的首地址。

那么像“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。一个使用全名称,一个使用简名称

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)类变量的值是所有对象共享的,而实例变量的值是每个对象独立的

5、成员变量的内存图

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

在这里插入图片描述

区域名称作用
程序计数器程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址
本地方法栈当程序中调用了native的本地方法时,本地方法执行期间的内存区域
方法区存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
堆内存存储对象(包括数组对象),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 + "日");
	}
}

在这里插入图片描述

6 成员方法

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

6.1 方法的概念

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

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

6.2 方法的原则

方法的使用原则:

(1)必须先声明后使用

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

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

6.3 成员方法的分类

成员方法分为两类:

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

6.4 如何声明方法

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

2、语法格式

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

声明位置示例:

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

错误示例:

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

6.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;
	}
}
(3)总结
  • 形参:在定义方法时方法名后面括号中声明的变量称为形式参数(简称形参)即形参出现在方法定义时。
  • 实参:调用方法时方法名后面括号中的使用的值/变量/表达式称为实际参数(简称实参)即实参出现在方法调用时。

总结:

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

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

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

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

6.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();//正确
    }
}

6.7 方法调用内存分析

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

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

示例:

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;
				}
			}
		}
	}
}

在这里插入图片描述

6.8 方法的参数传递机制

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

示例代码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:

class Test{
    public static void change(MyData my){
        my.num *= 2;
    }
    
    public static void main(String[] args){
        MyData m = new MyData();
        m.num = 1;
        
        change(m);//调用完之后,m对象的num属性值就变为2
    }
}

class MyData{
    int num;
}

示例代码3:

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;
				}
			}
		}
	}
}

陷阱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对象不可变,一旦修改就会产生新对象
	}
}

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

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

6 可变参数

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

格式:

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

要求:

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

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

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

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

示例一: 求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;
	}
}

7 方法重载

  • 方法重载:指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。
  • 参数列表:数据类型个数不同,数据类型不同,数据类型顺序不同。
  • 重载方法调用:JVM通过方法的参数列表,调用不同的方法。
示例:判断两个方法是否是合理的重载方法
//判断如下两个方法是否构成重载:是
class StringUtil{
	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;
	}
	public static String concat(String[] args){
		String str = "";
		for (int i = 0; i < args.length; i++) {
			str += args[i];
		}
		return str;
	}
}
//判断如下两个方法是否构成重载:不是
class Count{
	public static int getSum(int... nums){
		int sum = 0;
		for (int i = 0; i < nums.length; i++) {
			sum += nums[i];
		}
		return sum;
	}
	public static int getSum(int[] nums){
		int sum = 0;
		for (int i = 0; i < nums.length; i++) {
			sum += nums[i];
		}
		return sum;
	}
}
class Test06_Overload_Problem2{
	public static void main(String[] args){
		System.out.println(sum(1,2));//(int a, int b)
		System.out.println(sum(1,2,3));//(int... args)和(int a, int... args)都兼容,就有问题
	}

	//不调用编译没问题,但是调用时就有问题
	public static int sum(int a, int b){
		return a+b;
	}
	public static int sum(int... args){
		int sum = 0;
		for(int i=0; i<args.length; i++){
			sum += args[i];
		}
		return sum;
	}
	public static int sum(int a, int... args){
		int sum = a;
		for(int i=0; i<args.length; i++){
			sum += args[i];
		}
		return sum;
	}	
}

8 命令行参数(了解)

通过命令行给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

9 static关键字

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

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

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

10 递归

  • 递归:指在当前方法内调用自己的这种现象。
  • 递归的分类:
    • 递归分为两种,直接递归和间接递归。
    • 直接递归称为方法自身调用自己。
    • 间接递归可以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、描述:猴子吃桃子问题,猴子第一天摘下若干个桃子,当即吃了所有桃子的一半,还不过瘾,又多吃了一个。第二天又将仅剩下的桃子吃掉了一半,又多吃了一个。以后每天都吃了前一天剩下的一半多一个。到第十天,只剩下一个桃子。试求第一天共摘了多少桃子?

在这里插入图片描述

  public static void main(String[] args) {
        int num = eat(1);
        System.out.println(num);
    }

    private static int eat(int day) {
        if (day == 10) {
            return 1;
        }
        return (eat(day + 1) + 1) * 2;
    }

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

在这里插入图片描述

 public static void main(String[] args) {
        int step = steps(4);
        System.out.println(step);
    }
    private static int steps(int i) {

        if (i <= 2) {
            return i;
        }

        return steps(i - 1) + steps(i - 2);

    }

在这里插入图片描述

3、

在这里插入图片描述

11 对象数组

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

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

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

示例一:

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

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

class Test16_ObjectArray{
	public static void main(String[] args){
		//要在数组中存储5个圆对象
		//声明一个可以用来存储圆对象的数组
		Circle[] arr = new Circle[5];
		//for(int i=0; i<arr.length; i++){
		//	System.out.println(arr[i]);
		//}
		//System.out.println(arr[0].radius);//NullPointerException
		
		//给元素赋值
		//元素的类型是:Circle,应该给它一个Circle的对象
		//arr[0] = 1.2;//错误的
		//arr[0]相当于它是一个Circle类型的变量,也是对象名,必须赋值为对象
		/*
		arr[0] =  new Circle();
		arr[0].radius = 1.2;
		System.out.println(arr[0].radius);
		*/
		
		//创建5个对象,半径随机赋值为[1,10)的随机值
		//Math.random()==>[0,1)
		//Math.random()*9==>[0,9)
		//Math.random()*9+1==>[1,10)
		for(int i=0; i<arr.length; i++){
			arr[i] = new Circle();//有对象才有半径
			arr[i].radius = Math.random()*9+1;
		}
		
		
		//遍历显示圆对象的信息
		for(int i=0; i<arr.length; i++){
			//arr[i]是一个Circle的对象,就可以调用Circle类中的属性和方法
			System.out.println(arr[i].getInfo());
		}
	}
}
class Circle{
	double radius;
	public double getArea(){
		return 3.14 * radius * radius;
	}
	public double getPerimeter(){
		return 3.14 * 2 * radius;
	}
	public String getInfo(){
		return "半径:" + radius +",面积:" + getArea() + ",周长:" + getPerimeter();
	}
}

对象数组的内存图分析

在这里插入图片描述

在这里插入图片描述

普通的成员变量和静态变量:

普通的成员变量  只要创建了对象,就会给每一个变量分配内存

静态变量是可以多个对象共享的
  1. 开辟内存空间时间不同

    普通属性	在创建对象的时候开辟空间
    
    静态属性	在类初始化(类加载)时进行空间开辟		
    
    					静态属性开辟空间早于普通属性
    
  2. 调用方式不同

    普通属性 对象名.属性名

     静态属性	类名.属性名(推荐)
    
     					对象名.属性名
    
  3. 在内存中的位置不同

    普通属性	堆内存
    
    静态属性	方法区
    
  4. 在内存中的份数不同

    普通属性	每个对象一份
    
    静态属性	所有对象共享一份
    
  5. 销毁时间不同

    普通属性	当没有对象引用时  会被垃圾回收器回收
    
    静态属性	生命周期和类保持一致
    
  6. 都有默认值

    int	0
    
    byte	o
    
    short	0
    
    long	0L;
    
    float	0.0F;
    
    double	0.0;
    
    char	'\u0000'
    
    String	null
    
    Person	null
    

方法之声明不调用,不会执行

方法之间是兄弟关系,不能进行嵌套

方法的形参和实参:

在哪里调用方法,那里就是方法调用处

形参:

处于方法的声明处,告诉方法调用处,想要调用当前方法,必须传值

规定方法调用处传递值的类型

实参:

处于方法调用处

给形参赋值

在这里插入图片描述
在这里插入图片描述

普通方法:

不用static修饰的方法,只能借用对象的调用才能执行

普通成员变量与局部变量的区别

1.是否含有默认值

​ 成员变量有默认值

​ 局部变量没有默认值 局部变量要想使用必须显示的初始化

2.存储位置不同

​ 成员变量 堆

​ 局部变量 栈

3.初始化时间不同

​ 局部变量 当方法调用后,才会有值

​ 成员变量 对象创建后就会有值

4.销毁时间不同

​ 局部变量 当方法执行完毕后 就会弹栈消失

​ 成员变量 当没有对象引用内存时 会被垃圾回收站回收

5.作用域不同

​ 局部变量 只在当前方法内有效

​ 使用时 必须完成初始化

​ 成员变量 当前类中所有的实例方法都可以使用

可变参数 JDK1.5新特性

形参的个数是可变的 个数的范围是[0,n]

注意:

​ 1.类型必须一致

​ 2.所有的实参都要保存到数组内

​ 3.一个方法只能存在一个可变形参 可变形参 必须位于形参列表的最后

方法的重载

1.同一类中

2.同一方法名

3.不同的形式参数

​ 数量、顺序、类型

注意事项:

1.修饰符不是构成方法重载的条件

2.返回值类型的有无 不是构成方法重载的条件

main()方法中的String[] 是完成初始化的

main(String [] args) 中的 args是可以更改的

eg: main(String [] a)

递归:方法自己调用自己

符合要求的递归

1.有出口

2.不断向出口靠近

3.递归有一个下山、上山的过程

4.递归可能出现 栈溢出

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值