JavaSE-方法、this 指向、(非)静态代码块

方法定义

修饰符 返回值类型 方法名(参数类型,参数名){
	...
	方法体
	...
	return 返回值;
}
  • 修饰符:可选常用public / static。

  • 返回值类型:方法可能会存在返回值。returnValueType是方法的返回值的类型。有返回至请根据返回类型进行设置(基本数据类型),无返回值则void。

  • 方法名:遵循驼峰命名法,见名知意。

  • 参数类型:参数就是一个占位符,当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数可选,方法可以不包含任何参数。

    • 形参:在方法被调用时用于接受外界输入的参数。

    • 实参:调用方法时实际传给方法的参数。

  • 方法体:定义功能。

静态方法与非静态方法

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

静态方法和类一起加载,不能直接调用非静态方法。

静态方法可以通过类调用或者在main 方法中可以直接调用。

非静态方法与实例化对象相关,需要实例化对象后才能被调用。

public class Demo{
	public static void main(String[] args){
		fn();  // error "Non-static method 'say()' cannot be referenced from a static context"
		Demo.fn();  // error "Non-static method 'say()' cannot be referenced from a static context"
		fn2();  // 调用了静态方法
		Demo demo = new Demo();
		demo.fn(); // 调用了非静态方法
		demo.fn3(); // 调用了非静态方法
		Demo.fn2();  // 调用了静态方法
	}
	public void fn(){
		System.out.println("调用了非静态方法")
	}
	puclic static void fn2(){
		System.out.println("调用了静态方法")
	}
	public void fn3(){
		fn1(); // 非静态方法是可以互相调用的。
	}
}

方法调用

  1. 静态方法的调用
public class Main{
	public static void main(String[] args){
		Main.test();
		test();
	}
	public static void test(){
		
	}
}
  1. 非静态方法的调用
public class Main{
	public static void main(String[] args){
		Main main = new Main();
		main.test();
	}
	public void test(){
		
	}
}

方法重载

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ovcJtDs-1653532454463)(imgclip.png “imgclip.png”)]

public class Main{
	public static void main(String[] args){
		test(10.0,10.0);
		test(10,20);
	}
	public static void test(int a,int b){
		System.out.println(a+','+b);
	}
	public static void test(double a,double b){
		System.out.println(a+','+b);
	}
}

形参与实参

形参与实参的概念

  1. 形参:函数体中需要使用到的参数,目的用于接收调用该函数时传入的参数。

  2. 实参:实际的参数,就是调用方法传递的参数。

可变参数

一个方法中只能定义一个可变参数,它必须是方法的最后一个参数。任何普通参数都必须在它之前声明。

public class Demo{
	public static void main(String[] args){
		Demo demo = new Demo();
		int sum = demo.test(1,2,3);
		System.out.println(sum);
	}
	public static int test(int... numbers){
		int sum = 0;
		for(int i = 0; i < numbers.length; i++){
			sum += numbers[i];
		}
		return sum;
	}
}

递归方法

简单来说就是自己调用自己。

递归结构包含两个部分:

  • 递归头:什么时候不调用自身方法。如果没有递归头将会陷入死循环。

  • 递归体:什么时候调用自身方法。

一定要注意栈溢出带来的问题

public class Main{
	public static void main(String[] args){
		test();  //=> error,栈溢出
	}
	public static void test(){
		test();
	}
}

使用递归处理阶乘问题:

// 5! 5*4*3*2*1
public class Main{
	public static void main(String[] args){
		int a = test(5);
		System.out.print(a);
	}
	public static int test(int i){
		if(i==1){
			return 1;
		}else{
			return i * test(i-1);
		}
	}
}

值传递与引用传递

什么是值传递、引用传递

  1. 值传递:调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改将不会影响实际的参数。
public class Demo{
	public static void main(String[] args){
		int a = 1;
		Demo.changeA(a);
		System.out.print("print in main ->",a);
	}
	public static void changeA(int a){
		a = 10;
		System.out.println("print in changeA ->",a);
	}
}

----> 输出结果:
print in changeA ->10
print in main ->1
  1. 引用传递:调用函数时将实际参数的地址直接传递到函数中,这样函数中如果对参数进行修改将会影响实际的参数。
public class Demo{
	public static void main(String[] args){
		Demo2 demo2 = new Demo2();
		demo2.setName("hello");
		Demo.changeObj(demo2);
		System.out.println("print in main ->",demo2);
	}
	public static void changeObj(Object obj){
		obj.setName("world")
		System.out.println("print in changeObj ->",obj);
	}
}
public class Demo2{
	private String name;  // 封装一个私有属性
	public String getName(){
		return this.name;
	}
	public void setName(String name){
		this.name = name;
	}
}

----> 输出结果:
print in changeObj ->{name='world'}
print in main ->{name='world'}

经过上述的方法执行后,实参的值竟然被改变了,那按照上面的引用传递的定义,实际参数的值被改变了,这不就是引用传递了么。于是,根据上面的两段代码,得出一个新的结论:

Java的方法中,在传递普通类型的时候是值传递,在传递对象类型的时候是引用传递。但是,这种表述仍然是错误的。

public class Demo{
	public static void main(String[] args){
		String name = "hello";
		changeString(name);
		System.out.println("print in main ->",name);
	}
	public static void changeString(String name){
		name = "world";
		System.out.println("print in changeString ->",name);
	}
}

----> 输出结果:
print in changeObj ->world
print in main ->hello

在java 到底是值传递还是引用传递?

  • 错误理解一:值传递和引用传递区分条件是传递的内容,如果传递的是基本数据类型的值就是值传递,如果传递的是引用数据类型的值就是引用传递。

  • 错误理解二:java 就是引用传递。

答案只能是值传递!!

实际上在证明java 到底是值传递还是引用传递的过程中,验证方法就已经出错了,当然得到的结果就是错的。

为什么说是实验结果出错呢?本质上还是得从值传递和引用传递的根本区别入手:

值传递引用传递
根本区别会创建副本(复制一份)不会创建副本
执行效果函数中 无法改变原始参数函数中 可以改变原始参数

那么最终如何验证java 是值传递的?

public class Demo{
	public static void main(String[] args){
		Demo2 demo2 = new Demo2();
		demo2.setName("hello");
		changeObj(demo2);
		System.out.println("print in main ->",demo2);
	}
	public static void changeObj(Demo2 obj){
		obj = new Demo2();
		obj.setName("world");
		System.out.println("print in changeObj ->",obj);	
	}
}
public class Demo2{
	private String name;  // 封装一个私有属性
	public String getName(){
		return this.name;
	}
	public void setName(String name){
		this.name = name;
	}
}

----> 输出结果:
print in changeObj ->{name='world'}
print in main ->{name='hello'}

结论

  • java中方法参数传递方式是按值传递。

  • 如果参数是基本类型,传递的是基本类型的字面量值的拷贝。

  • 如果参数是引用类型,传递的是该参量所引用的对象在堆中地址值的拷贝。

main 方法

main 方法描述

public class Demo{
	public static void main(String[] args){
	
	}
}

【public】 :main 方法是启动时候由JVM 进行加载的,public 的可访问权最高,所以必须使用public 修饰

【static】:调用方法的方式可以是对象调用、类调用,而main 方法因为是JVM 调用所以无需生成对象,所以使用static 修饰

【void】:调用main 方法无返回值,所以使用void修饰

【main】:C语言规定使用main 作为方法名

【String[]】:此字符串数组用来运行时接受用户输入的数据;因为字符串在JAVA 中具有普遍性,所以使用字符串是最优选择;数组的话因为参数不只一个,所以使用String[] 作为参数

不过在JDK 1.5 引入动态参数后String[] args也可以使用String… args 来实现。

public class Demo{
	public static void main(String... args){

	}
}

main 方法的重载

public class Demo {
    public static void main(String args) {
        System.out.println("main");
    }

    public static void main(String[] args) {
        main("test");
    }
}

main 被其他方法调用

public class Demo {
    public static int count = 3;

    public static void main2(String[] args) {
        count--;
        main(args);
    }

    public static void main(String[] args) {
        System.out.println("main 方法执行次数:" + count);
        if (count <= 0) System.exit(0);
        main2(args);
    }
}

输出结果:

main 方法执行次数:3
main 方法执行次数:2
main 方法执行次数:1
main 方法执行次数:0

main 方法被子类继承重写

public class Demo {
    public static void main(String[] args) {
        System.out.println("父类main 方法");
    }
}

class Demo2 extends Demo{
    public static void main(String[] args) {
        System.out.println("子类main 方法");
    }
}

输出结果:

子类main 方法

this 指向

this 指向类的实例对象

  • this 修饰属性,this.[属性名] 给实例中的属性赋值。
  • this 修饰方法,this.[方法名] 调用实例对象中的方法。
  • this 修饰构造器,在构造器中直接调用 this([参数]) 传递到同名重载构造器中,且this修饰构造器必须放在第一行。
public class Main{
    public Main(){
        
    }
    // 有参构造器
    public Main(String name,int age){
        this(age);
        this.name = name;
    }
    public Main(int age){
       this.age = age;
    }
    public static void main(String[] args){
        Main m = new Main("haha",20);
        m.say('hello,world')
    }
    // 属性
    int age;
    String name;
    String word;
    public void say(String word){
        // this执行实例化本身
        this.word = word;
        System.out.println(this.name);
        System.out.println(this.age);
        System.out.println("他说:" + this.word)
    }
}

静态代码块

https://blog.csdn.net/qq_35868412/article/details/89360250

java 中的四种代码块

  1. 方法块:静态方法、非静态方法、构造方法

  2. 非静态代码块:非静态代码块会 在创建对象时被调用,每次创建时都会被调用,优先于构造方法执行。

  3. 静态代码块:与类一并加载,用static{}包裹起来的代码片段,只会执行一次。静态代码块优先于非静态代码块执行。

  4. 同步代码块:使用synchronized(){}包裹起来的代码块,在多线程环境下,对共享数据的读写操作是需要互斥进行的,否则会导致数据的不一致性。

静态代码块与非静态代码块的异同点

相同点:都是jvm 加载类之后构造函数执行之前执行,在类中可以定义多个,一般代码块中对一些static 变量进行赋值操作。注意是赋值操作,避免空指针异常

package com.example.demo;

import java.util.HashMap;

public class test2 {
    private static HashMap<Integer,String> hashMap = null;
    static {
        hashMap.put(1,"2");  // 对一个指针进行操作,语法错误
    }
    public static void main(String[] args) {
        System.out.println(hashMap);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wtCtVK3j-1653532590899)(imgclip.png “imgclip.png”)]

正确操作:

package com.example.demo;

import java.util.HashMap;

public class test2 {
    private static HashMap<Integer,String> hashMap = null;
    static {
        hashMap = new HashMap<>();  // 先赋值再操作
        hashMap.put(1,"2");
    }
    public static void main(String[] args) {
        System.out.println(hashMap);
    }
}

不同点:静态代码块优先执行于非静态代码块,静态代码块只在第一次实例化对象执行一次,之后不再执行。而非静态代码块每实例化一次执行一次。

面试题

请问输出情况:

public class Demo {
    public static String name = "静态属性";

    static {
        System.out.println(name);
        System.out.println("静态代码块");
    }

    {
        System.out.println("非静态代码块1");
    }

    public Demo() {
        System.out.println("无参构造器");
    }

    {
        System.out.println("非静态代码块2");
    }

    public void fn1() {
        System.out.println("非静态方法");
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        System.out.println("main 静态方法块");
        Demo demo2 = new Demo();
        demo2.fn1();
    }
}

---> 输出结果
静态属性
静态代码块
非静态代码块1
非静态代码块2
无参构造器
main 静态方法块
非静态代码块1
非静态代码块2
无参构造器
非静态方法

出现继承情况:

class Demo2 {
    public static String name = "静态属性";

    static {
        System.out.println("子类静态属性" + Demo1.name);
        System.out.println("Demo2 静态代码块");
    }

    {
        System.out.println("Demo2 非静态代码块1");
    }

    public Demo2() {
        System.out.println("Demo2 无参构造器");
    }

    {
        System.out.println("Demo2 非静态代码块2");
    }
}

class Demo1 extends Demo2 {
    public static String name = "静态属性";

    static {
        System.out.println(name);
        System.out.println("父类静态属性" + Demo2.name);
        System.out.println("静态代码块");
    }

    {
        System.out.println("非静态代码块1");
    }

    public Demo1() {
        // super();
        System.out.println("无参构造器");
    }

    {
        System.out.println("非静态代码块2");
    }

    public void fn1() {
        System.out.println("非静态方法");
    }
}

public class Demo {
    public static void main(String[] args) {
        Demo1 demo = new Demo1();
        System.out.println("main 静态方法块");
        Demo1 demo1 = new Demo1();
        demo1.fn1();
    }
}

---> 输出结果
子类静态属性null
Demo2 静态代码块
静态属性
父类静态属性静态属性
静态代码块
Demo2 非静态代码块1
Demo2 非静态代码块2
非静态代码块1
非静态代码块2
无参构造器
main 静态方法块
Demo2 非静态代码块1
Demo2 非静态代码块2
非静态代码块1
非静态代码块2
无参构造器
非静态方法

结论

父静态属性 -> 父静态代码块 -> 子静态属性 -> 子静态代码块 -> 子静态方法main -> 父非静态代码块 -> 父构造方法 -> 子非静态代码块 -> 子构造方法 -> 非静态方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值