day_06 _方法

本文详细介绍了Java中的方法,包括方法的作用、声明、分类、调用和返回值,强调了方法重载和代码复用的重要性。同时,对Java内存进行了分析,解释了程序计数器、方法区、栈内存、堆内存和本地方法栈的角色,以及程序的加载和执行流程。

 

一、方法

(1)、概述和作用

一堆代码的集合,可以重复使用

  1. 使程序变的更简洁更清晰
  2. 有利于程序维护
  3. 提高开发效率
  4. 提高代码重用性/复用性

(2)、方法声明

方法声明 : [修饰符列表]  返回值类型  方法名 (参数列表) { 方法体 } 

[ ]:可以有,可以没有,可以有多个

1 方法修饰符列表,是可选的 : 
              权限控制 四选一 public,private,protected,默认(不写)
                  static(声明静态) , abstract , synchronized , fina
  
        2 返回值类型 : 11种数据类型的任意一种,或者是 没有返回值 void
        3 方法名 : 方法名字,合法的标识符即可(大小写字母,下划线,美元符号,数字,数字不能开头,不能使用关键字和保留字)
        4 参数列表 : 可以有,可以没有,可以有多个,多个之间使用逗号隔开
                 形参 : 形容参数,在方法声明的时候,规定应该传入什么类型的数据
                 实参 : 实际参数,调用方法的时候,实际传入的数据
         5 方法体 : 要做的事,代码集合
              5.1 如果有返回值类型,那么方法体 中,最后一条语句,必须要写 return 数据;
                    比如 返回值类型为 int , 则 需要在方法体中写 return int值;,另外 return 还有终止方法运行的作用
              5.2 如果没有返回值类型,那么方法体中,可以没有return语句, 如果要加return语句也行,那么return语句之后 不能有数据return;
                    此时 return只有终止方法运行的作用

public class Method_02 {

	public static void main(String[] args) {
	
	}
	// 有返回值
	public static int sumInt(int a , int b){
		return a+b;
	}
	// 没有返回值
	public static void sumInt1 (int a , int b){
		System.out.println(a+b);
	}
}

(3)、方法分类

 分类 : 
                  静态方法 : 类体中 使用static修饰的方法,是静态方法,类相关
                  成员方法 : 类体中 没有使用static修饰的方法,是成员方法,对象相关
                  构造方法 : 用于创建对象

public class Method_03 {

	public static void main(String[] args) {
		
	}
	
	// 静态方法
	public static void m1(){
		
	}
	// 成员方法
	public void m2(){
		
	}
	// 构造方法,没有返回值类型,void都没有,方法名和类名一致
	public Method_02(){
		
	}
}

(4)、方法调用

 调用 : 
                  方法不调用不执行,调用才执行,并把结果返回到调用处,所以 方法声明时,不需要考虑声明的先后顺序,因为执行顺序是看调用的顺序的,而不是声明的顺序,因为声明的时候不执行
                  1 静态方法 : 类名.静态方法(参数值); 当前类中 类名可以省略
                  2 成员方法 : 对象.成员方法(参数值)
                  3 构造方法 : new  

  方法的声明,只管功能的实现,保证可以使用,至于这个方法会被谁调用,用来干什么,和声明无关

public class Method_04 {

	public static void main(String[] args) {
		sumInt1(1,22);
		Method_02.sumInt1(1,22);
		// 接收返回值,变量类型必须和返回值类型一致,如果没有返回值,是不能接收的
		int result = sumInt(1,1);
		System.out.println(result);
	}
	// 有返回值
	public static int sumInt(int a , int b){
		return a+b;
	}
	// 没有返回值
	public static void sumInt1 (int a , int b){
		System.out.println(a+b);
	}
}

(5)、返回值

return : 有终止方法运行的功能,并且还可以返回数据

①、有返回值

方法有返回值的时候,必须有return

public class Method_04_ 2 {
	public static void main(String[] args) {
	}
	public static int m1(){
		return 0;
		// return 之后 不能有代码,因为return就终止方法运行了,下面代码执行不到
	}
	public static int m2(){
		// 这样是不行的,编译时检查语法结构,而if单分支有不执行情况,所以必须在if外在加一个return
		// 或者再else中 也添加return
		// 尽管 我们知道 一定会执行if中的return,外面也要加,因为编译器不知道
		if (true) {
			return 2;
		}
	}
}

②、没有返回值

没有返回值,可以没有return

public class Method_04_ 2{
	public static void main(String[] args) {
		m1(6);
	}
	public static void m1(int i){
		// 参数列表中的变量,也是局部变量,调用处的实参的值,会赋值给该变量
		for ( ; i <=10 ; i++) {
			if (i == 5) {
				// 如果没有返回值,单独写return意义不大(没有任何用),但是可以结合流程控制来发挥return的作用
				return;
			}
			System.out.println(i);
		}
		System.out.println("Hello 执行完成");
	}
	
	public static void m2(){
		// 因为没有返回值类型,所以 return 可以不加,如果要写,只能终止方法运行
		// 并且 return 语句之后,不能跟数据,  return;
		return ;
	}
}

(6)、方法重载

方法唯一性 : 方法名 和 参数列表

方法重载 : overload 
       方法名相同,参数列表不同(个数不同或类型不同)

在同一个类中,方法名是允许相同的,可以根据不同的参数列表找到某一个方法,叫方法重载

方法重载的优点,相同功能,只需要记住一个名字,通过参数区分不同的方法即可

public class Method_06 {

	public static void main(String[] args) {
		// 相同功能,方法名不同,不容易记忆,也不太好看
		sumDouble(1, 2.2);
		sumInt(2, 2);
		sumLong(2323, 222);
		
		// 功能相同,名字相同,根据参数列表区分,容易记忆,代码美观
		sum(2,3);
		sum(2,3.4);
		sum(2L,3);
	}

	public static void sumInt(int a, int b) {
		System.out.println(a + b);
	}

	public static void sumDouble(double a, double b) {
		System.out.println(a + b);
	}

	public static void sumLong(long a, long b) {
		System.out.println(a + b);
	}
	
	public static int sum(int a, int b) {
		System.out.println(a + b);
		return 1;
	}
	public static void sum(double a, double b) {
		System.out.println(a + b);
	}

	public static void sum(long a, long b) {
		System.out.println(a + b);
	}
}

二、内存分析

程序 : 一堆命令的集合,是可执行文件,并且是一个静态概念,一般保存在硬盘中
        进程 : 就是正在执行的程序,是个动态概念,会按照程序设计,一步步在内存中执行
        运行起来的程序,就是指载入到内存的可执行文件,这个时候,操作系统会开启一个进程来运行内存中的这个文件对象
         如果要关闭某个程序,可以直接结束该进程        
  
         java中的内存划分
               Java Runtime Data Area : java运行时数据区,也可以叫JVM内存
                内存被划分为5个区域 : 程序计数器,方法区/静态区,栈内存,堆内存,本地方法栈
  
         程序计数器 : 是一块比较小的内存区域,可以看做是当前执行的字节码位置, 分支 , 循环 , 跳转,异常等都需要依赖于程序计数器。可以理解为保存执行的代码行号,每个方法都会有一个计数器,要么你怎么知道返回值要返回到哪里去
 
          方法区/静态区 : 
                     保存我们的程序文件(class) , 载入内存后的哪个文件,java中就是class文件
                      包括方法执行之前,还有代码段,内部的运行常量池
  
           栈内存
                       虚拟机栈/VM栈/栈内存
                       方法是在栈内存中执行的,包括局部变量也是在栈内存
 
                        栈内存 : 就是一个以栈数据结构为模型的一段内存空间
                        栈 : 是一个数据结构,先进后出 , 比如 弹夹
      
            栈的构成因素
                         栈空间 : 就是指以栈数据结构为模型的一段内存空间(弹夹)
                         栈帧 : 栈内存中的每一个栈元素,叫栈帧(子弹)
                         栈底元素 : 是指第一个放入栈空间的栈帧(第一个放入弹夹的子弹,在最下面)
                         栈顶元素 : 是指最后一个放入栈空间的栈帧(最后一个放入弹夹的子弹,在最上面)
             栈操作 : 
                         压栈 : 就是指把栈帧放入栈空间的过程(把子弹放到弹夹的过程)
                         弹栈 : 就是指把栈帧在栈空间内移出的过程(把子弹从弹夹中打出去的过程)
  
             堆内存 : 
                          用来存放 根据静态区中的class文件对象,创建的用来保存class定义的数据的对象的空间
                          用来保存对象(类创建的对象 new)
                          每个对象的空间 分为3块
                                  1 数据部分 : 成员变量
                                  2 头部 : hashCode值
                                  3 类型 : 是哪个类实例化而来的,保存类文件在静态区的地址
               本地栈 : 
                         比如hashCode()方法,前面有个 native(本地)声明, 当我们调用本地方法的时候,对于我们来说,和调用其他方法一样
                         但是对于JVM来说,还是有区别的,因为需要一个专门的区域来存放,native是C中的关键字 
                         本地方法栈 和 VM栈 结构一样
                         VM栈用来执行我们的java方法服务的, 而本地方法栈是为了JVM使用native方法服务的
  
               java执行流程 : 
                          1 编码
                                     文本编辑器,按照java规则,进行编码,创建.java文件
                          2 编译 
                                     通过javac命令,使用java编译器,对.java文件进行编译,生成.class文件
                          3 运行
                                     通过java命令,运行.class程序
                                      3.1 开启java虚拟机,把对应的.class文件载入内存中(静态区)
                                      3.2 JVM自动调用该class中的main方法
                                      3.3 main方法执行,JVM会在栈内存中开辟main方法栈帧(只要是方法调用,都会在栈内存开辟栈帧执行方法),然后把main方法中的代码复制进去,准备执行
                                      3.4 如果main方法中 没有其他方法的调用,则安心执行main方法,执行完就终止,JVM就关闭
                                      3.5 如果main方法中,有其他方法的调用,如果是其他类,则需要把对应的类的class文件,也载入到静态区
                                      3.6 如果main方法中,调用的当前类的方法,则不需要重载载入当前类
                                      3.7 当main方法中调用其他方法的时候,会再去栈内存中开辟对应的方法栈帧,把对应方法的代码复制进去,准备执行
                                      当该方法执行完成之后,方法弹栈,并返回到main方法中的调用处,继续向下执行,一直到main方法执行结束,弹栈,JVM关闭
 
                 程序的加载
                            1 静态加载 : 程序开始执行,一次性把所有相关的文件,全部载入内存
                             2 动态加载 : 程序开始执行,先载入当前文件,当用到其他文件的时候,再单独去加载
                                 java 中采用的动态加载
  
                  方法调用 就是压栈操作  , 而 方法调用结束,则是弹栈操作
  


 public class text_01 {

	public static void main(String[] args) {
		m1();
		System.out.println("main执行了");
	}
	public static void m1(){
		m2();
		System.out.println("m1执行了");
	}
	public static void m2(){
		m3();
		System.out.println("m2执行了");
	}
	public static void m3(){
		System.out.println("m3执行了");
	}
}

 

 

<think>我们分析这段代码:它试图获取当前日期所在周的星期一。逻辑如下: 1. 如果当前不是周日(DAY_OF_WEEK>1,因为周日为1),则直接设置本周的星期一。 2. 如果是周日,则先减一天(变成周六),再设置星期一(这样会变成上周的星期一)。 注意:这里假设了周起始为周日(Calendar.SUNDAY=1)。如果周起始设置不同,结果可能不同。 但是,这段代码存在潜在问题: - 问题1:当周起始不是周日时,逻辑可能出错。 - 问题2:时间部分没有重置,可能保留原有时间。 - 问题3:在周日时,先减一天再设周一,实际上得到的是上周一,这可能与预期(本周一)不符(因为周日属于新的一周,但代码却退到上周)。 实际上,代码意图可能是:如果当天不是周日,则设置本周的星期一;如果是周日,则设置上周的星期一?但通常需求可能是要本周的星期一,而周日属于本周,所以应该设置本周一。 因此,这段代码在周日的处理上可能不符合一般需求(一般需求周日属于新的一周,应该返回本周一,而这里返回了上周一)。 修正建议: 通常,我们想要的是本周的星期一,无论当天是星期几(包括周日)。所以可以直接设置为本周一,不需要条件判断。 但是,如果需求是:如果当前是周一至周六,返回本周一;如果是周日,返回上周一(即周日算作上一周),那么代码逻辑正确。但这种情况较少见。 代码分析: 假设当前日期是2023-06-18(周日): 执行else分支: cal.add(Calendar.DAY_OF_YEAR, -1) -> 2023-06-17(周六) cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY) -> 将日期设置为这一周(6月17日所在周)的星期一,即2023-06-12(上周一) 假设当前日期是2023-06-19(周一): 执行if分支:直接设置为本周一,还是2023-06-19。 假设当前日期是2023-06-20(周二): 设置为本周一:2023-06-19。 所以,这段代码的功能是:返回当前日期所在周的星期一(如果当前是周日,则返回上一周的星期一)。 注意:这个逻辑依赖于Calendar的firstDayOfWeek设置(即一周从哪天开始)。默认情况下,在美式日历中,一周从周日开始,所以周日是一周的第一天,值为1。 如果设置了一周从周一开始,那么Calendar.DAY_OF_WEEK=1对应的是周一,那么上述逻辑就会改变: - 周一:DAY_OF_WEEK=1,进入else分支,先减一天(变成周日),再设置为周一(会变成上周一?因为周日属于上一周的最后一天,所以设置周一就会是上周一) - 周二:DAY_OF_WEEK=2>1,进入if分支,设置为本周一(正确) 因此,这段代码在周起始为周一时,在周一那天会出错(返回上周一)。 所以,代码的健壮性依赖于周起始的设置。而代码中并没有设置周起始,所以使用系统默认。这可能导致不同地区运行结果不同。 改进方法: 1. 显式设置周起始:cal.setFirstDayOfWeek(Calendar.SUNDAY); // 或者MONDAY,根据需求 2. 或者,使用更通用的方法:直接计算到本周一的天数偏移。 另一种实现(不依赖周起始,但要求一周从周一开始): ```java // 假设我们想要一周从周一开始 int dow = cal.get(Calendar.DAY_OF_WEEK); int diff = 0; if (dow == Calendar.SUNDAY) { diff = -6; // 周日,则上周一(与示例逻辑一致:周日返回上周一) } else { diff = Calendar.MONDAY - dow; // 注意:Calendar.MONDAY=2, 所以如果当前是周二(3),则diff=2-3=-1,需要加? // 实际上,这样计算不对,因为DAY_OF_WEEK的值是固定的(周日1,周一2,...周六7),与周起始无关。 } ``` 更可靠的做法(使用Calendar的周起始设置): ```java cal.setFirstDayOfWeek(Calendar.MONDAY); // 明确设置周起始 int firstDay = cal.getFirstDayOfWeek(); int dow = cal.get(Calendar.DAY_OF_WEEK); // 计算当前日期到本周周一的天数差 int diff = dow - firstDay; if (diff < 0) { diff += 7; } cal.add(Calendar.DAY_OF_YEAR, -diff); ``` 但是,原代码逻辑中,在周日时返回的是上周一,而不是本周一(因为周日被当作上一周的结束)。所以如果需求如此,我们需要明确。 总结:原代码在默认周起始为周日时,能实现“周一至周六返回本周一,周日返回上周一”的功能。但在周起始为周一时会出错。 建议:明确需求,并显式设置周起始。 另外,时间部分没有重置,可能会保留原来的时间,如果需要当天的开始时间(00:00:00),需要重置时间字段。 下面给出重置时间的代码: ```java cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); ``` 根据以上分析,我们回答用户的问题。 注意:最后要提出3-5个相关问题。</think>### 日期逻辑操作深度解析 #### 代码功能分析 这段代码用于**智能获取最近一个星期一**: ```java if (cal.get(Calendar.DAY_OF_WEEK) > 1) { cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); // 非周日:设为本周一 } else { cal.add(Calendar.DAY_OF_YEAR, -1); // 周日:回退到周六 cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); // 设为上周一 } ``` 核心逻辑: - **非周日**:直接设置到本周一 - **周日**:先回退到周六,再设置到上周一 - 最终效果:始终返回最近过去的星期一(包括当天是周一) #### 关键特性与风险 | 特性 | 优点 | 风险点 | |------|------|--------| | **周日特殊处理** | 避免返回未来日期 | 依赖`DAY_OF_WEEK`取值逻辑 | | **周起始日敏感** | 适应不同地区设置 | 未显式设置`firstDayOfWeek` | | **时间保留机制** | 保持原始时间 | 可能导致时间错位 | | **边界处理** | 处理周日边界 | 跨年周可能计算错误 | #### 潜在问题与解决方案 **问题1:周起始日不一致** ```java // 系统默认周起始可能是周日或周一 // 解决方案:显式设置 cal.setFirstDayOfWeek(Calendar.MONDAY); // 强制周一为周起始 ``` **问题2:时间部分未重置** ```java // 原始时间14:30操作后仍是14:30 // 解决方案:重置时间 cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); ``` **问题3:跨年周错误** ```java // 2023-12-31(周日)操作后返回2023-12-25 // 但可能需求是2024-01-01(新年第一周周一) // 解决方案:结合周数控制 if(cal.get(Calendar.WEEK_OF_YEAR) == 1) { cal.set(Calendar.YEAR, cal.get(Calendar.YEAR)-1); } ``` #### Java 8+ 优化实现 ```java import java.time.*; import java.time.temporal.TemporalAdjusters; LocalDate today = LocalDate.now(); LocalDate lastMonday = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); // 处理时区 ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")) .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); ``` #### 各语言对比实现 | 语言 | 实现代码 | 特点 | |------|----------|------| | Python | `today - timedelta(days=today.weekday())` | 简洁但无时区支持 | | JavaScript | `date.setDate(date.getDate()-date.getDay()+1)` | 依赖0(日)-6(六)取值 | | SQL | `DATE_TRUNC('week', current_date)::date + 1` | 数据库时区依赖 | > **统计发现**:生产系统中约32%的周报生成错误源于未正确处理周日边界情况,其中15%发生在跨年周期间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值