0722(015天 面向对象编程基础05-复习、函数传参、包装类、常量池、多态)

0722(015天 )

每日一狗(边牧王可爱

在这里插入图片描述

主标题

复习:

  • 主方法

    • 在运行的时候在类的后面填点参数啥的
  • 构造方法

    • 在一个对象的生命周期中值运行一次
    • 重载(系统只认识类型不同,形参不同没什么卵用,系统认为还是一样的)
    • 配合new和传入参数来使用
  • 析构器(没什么卵用)

    • 垃圾回收前运行的(而且还不保证运行)
  • final修饰符:赋值了就不能变了(栈级别)

    • 基本数据类型:只能赋值一次
    • 引用数据类型:引用只能赋值一次(及不能改引用,但是可以改引用对象的属性或者方法啥的)
  • static修饰符:

    • 修饰类属性:就是类的属性(公共属性,只会定义一次,所有对象共享一个属性)
    • 修饰类方法:那就是类的方法
    • 当然这个类实例化的对象也可以点出来用;(会有警告信息)
  • 类的静态常量属性:可以创建时赋值,也可以在静态代码块中进行延迟赋值(其他的度不行)

  • 类的静态方法:只能直接访问静态成员(非静态成员属性那是人家new后才有的,你自己可以不new就能用,我还没创建你就想用,想屁吃呢,想访问,自己构造对象去。)

  • 设计模式:

    • 是最佳方案(某种情况下解决某类问题)
    • eg:单例设计模式(饿汉)
      • 优点:没有枷锁1,执行效率高;缺点:浪费内存
      • 构造器私有化
      • 静态属性构造一个属性,存一个对象
      • 在有一个静态的公共方法,你调用我就把这个我构造静态属性给你。
  • 对象的创建执行流程

    • 静态属性->属性->构造器
  • 继承

    • 静态属性或者方法是可以被继承的(原本就可继承)

1. 静态修饰符扩展(static)

new一个对象时的优先级:静态的属性或代码块>非静态属性和代码块>构造器

1.1 代码块

静态

类在执行时需要通过一个叫作类加载器的组件将程序加载到内存中,类在运行时一般不会发生变 化,所以类不会频繁加载,

在整个运行过程中只加载一次,而且常驻内存 静态块在类加载完毕后自动执行,而且只执行一次

非静态

非静态块在类内且在所有的方法之外,非静态块并不会在类加载后自动执行,而是在构建当前对象时自 动执行。

new一次则会执行一次,执行时机在构造器之前执行。

结论

声明一个对象为某个类的时候并不会将类加载到内存中,应该只是进行检索名称匹配,将一个类加载到内存中时会优先执行其中的被static修饰的属性和代码块,从上至下按序执行。

package com.静态测试;

public class Test01 {
	public static void main(String[] args) throws Exception {
		A5 aa; 
		Class.forName("com.静态测试.A5");
		/*
		 * A5...static
		 * A5 static2
		 */
		A5 ab = new A5();
		/*
		 * A5...Non-static
		 * A5构造器
		 */
	}
}

class A5 {
	// 静态块,没有覆盖定义
	static {
		System.out.println("A5...static");
	}
	// 非静态代码块
	/*
	 * 非静态块在类内且在所有的方法之外,非静态块并不会在类加载后 自动执行,而是在构建当前对象时自动执行。new一次则会执行一 次,执行时机在构造器之前执行
	 */
	{
		System.out.println("A5...Non-static");
	}

	// 构造器
	public A5() {
		System.out.println("A5构造器");
	}

	static {
		System.out.println("A5 static2");
	}
}

1.2 不当人考点

考点yan2.Test6

优先级:静态的->非静态->构造器

package com.静态测试;

//考试点
/*
* 当类加载完毕会自动优先处理static属性和static块,这两个优先级
* 是相同的,所以谁在前先处理谁
* 
* new对象时,处理非静态属性和非静态块,这两个优先级是相同的,所以
* 谁在前先处理谁。最后执行构造器
* 
* 先父后子
*/
public class Test02 {
	public static void main(String[] args) {
		new B6();
		/*
		 * A6 static
		 * B6 static
		 * D6构造器
		 * E6构造器
		 * A6 Non-static
		 * A6构造器
		 * C6构造器
		 * B6 Non-static
		 * B6构造器
		 * F6构造器
		 */
	}
}

class A6 {
	private E6 ee = new E6();
	private static F6 ff = new F6();
	{
		System.out.println("A6 Non-static");
	}

	public A6() {
		System.out.println("A6构造器");
	}

	static {
		System.out.println("A6 static");
	}
}

class B6 extends A6 {
	static {
		System.out.println("B6 static");
	}
	private C6 cc = new C6();
	{
		System.out.println("B6 Non-static");
	}
	private static D6 dd = new D6();

	public B6() {
		System.out.println("B6构造器");
	}
}

class C6 {
	public C6() {
		System.out.println("C6构造器");
	}
}

class D6 {
	public D6() {
		System.out.println("D6构造器");
	}
}

class E6 {
	public E6() {
		System.out.println("E6构造器");
	}
}

class F6 {
	public F6() {
		System.out.println("F6构造器");
	}
}

1.3 使用意见

yan2.Test7

  • 静态方法只能访问静态成员,静态有访问局限性
  • 静态方法中不能有this super关键字
  • 主函数是静态的
  • 属性共享时使用
  • 没有访问特有数据时使用
  • 类内功能都是静态的,则构造方法需要私有化(没必要,节约空间)

1.4 静态导包

静态导包直接用方法名,不用先导个类,然后再用类的方法

package com.静态测试;

//import java.lang.Math.abs; // 导入 java.lang.Math.abs 无法解析
import static java.lang.Math.max;

public class Test03 {
	public static void main(String[] args) {
		System.out.println(max(20, 64));
	}
}


2. 不确定数量传参

2.1 数组传参

调用时好歹的好待个参数,空指针和空数组都行

yan3.test1

package com.yan3;

//方法中的可变长个数的参数
public class Test1 {
	public static void main(String[] args) {
		A1 aa = new A1();
		int res = aa.max(null);
//		int res = aa.max(); // 可以传空数组、空指针,但是不能不传
		System.out.println(res);
		res = aa.max(new int[] {});
		System.out.println(res);
		res = aa.max(new int[] { 1, 2, 3, 4, 0, 12, 56, 89, 34, 45 });
	}
}

class A1 {
	// 定义一个获取最大值的方法,可以传入多个整型参数,具体参数格式未知。如果没有参数则返回整型的最小值

	// 方案1:使用数组
	public int max(int[] args) {
		int res = Integer.MIN_VALUE;
		if (args != null && args.length > 0) {
			for (int i = 0; i < args.length; i++) {
				if (args[i] > res) {
					res = args[i];
				}
			}
		}
		return res;
	}
}

2.2 语法糖,底层还是数组

必须是最后一个参数,那也就是只能有一个,类型相同,

package com.yan3;

//方法中的可变长个数的参数
public class Test1 {
	public static void main(String[] args) {
		A1 aa = new A1();
		int res=aa.max2();
		System.out.println(res);
		res = aa.max2(1, 2, 3, 4, 0, 12, 56, 89, 34, 45);
		System.out.println(res);
	}
}

class A1 {
	// 定义一个获取最大值的方法,可以传入多个整型参数,具体参数格式未知。如果没有参数则返回整型的最小值
	//方案2:使用不确定个数的参数。可以理解为一种语法糖。起始内部的处理细节还是数组
	public int max2(int... args) {
		int res = Integer.MIN_VALUE;
		if (args != null && args.length > 0) {
			for (int i = 0; i < args.length; i++) {
				if (args[i] > res) {
					res = args[i];
				}
			}
		}
		return res;
	}
	//注意:不确定个数的参数必须是方法声明时最后一个参数
//	public void pp(int... args1,String... str2) {}  错误,因为2个
//	public void pp4(int... tt,String name) {}
	public void pp2(String name,int...args) {}
	public void pp3(String name,String...args) {}
}

3. 成员应用细节

在这里插入图片描述

本地方法栈:有些不是用java实现的程序如c,

解释器(java虚拟机)是平台相关的,它认识所有的字节码文件

编译器是平台无关的,它可以将文本文件编译为字节码文件

3.1 引用和指针的区别

引用、句柄:这就是个用地址算出来的玩意儿(哈希值),在虚拟机里它认,但是出了这个虚拟机,没有软件能认识他,通过一个上面的本地方法栈中的一个用c编的程序搞出来的。

指针、地址:这个是真实存在并且可以运算和操作的物理内存地址,拿来我就能改数据,而且通过地址的运算我还能改旁边的数据。

3.2 变量分类

  • 局部临时变量:方法体中声明(栈)
  • 成员变量:方法体外、类内声明
    • 实例变量:没有static、各个实例对象中相互隔离(堆)
    • 静态变量:有static、应该是这个类的属性,各个实例对象之间共享(方法区内存)
  • 三大内存空间变化最频繁的是栈内存,最先有数据的是方法区内存,垃圾回收器主要针对的是堆内存

3.3 垃圾回收到底什么时候进行垃圾回收

堆内存中的对象称为垃圾数据的时候会被GC回收

什么时候堆内存对象会变成垃圾?

  • 引用数计数器为0时
  • 可达性算法(引用链法):从GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一 个对象到GC Roots没有任何引用链相连时,则证明此对象是可以被回收的。

3.4 方法的问题

方法的分类:

  • 无参无返(没有参数列表,没有返回值)单纯的作为 功能代码的聚合使用 便于功能复用
  • 无参有返(没有参数列表,有返回值)
  • 有参无返(有参数列表 没有返回值)适用于功能需要根据参数来进行计算的情况,但是计算的最终 结果又无需返回处理
  • 有参有返(有参数列表,有返回值)适用于功能需要根据参数来进行计算的情况,而且最终的结果 需要返回处理

方法的形参和实参:

  • 形参 :是定义在方法声明上,用于指定该方法需要传递的参数类型的
  • 实参 :是调用方法时,实际传递的参数值

方法参数传递

官方只认这里是值拷贝。我感觉很合理呀,只不过这是栈范围的值拷贝(非常合理)。

  • 栈中对于简单数据类型进行值存储
  • 栈中对于引用数据类型进行引用的存储

3.5 栈和堆的区别

  • 管理方式:栈自动释放,堆需要GC
  • 空间大小:栈比堆小 碎片相关:
  • 栈产生的碎片远小于堆
  • 分配方式:栈支持静态和动态分配,而堆仅仅支持动态分配
  • 效率:栈的效率比堆高

new出来的对象和这个对象的实例变量存在堆中

一般情况下都是堆+栈来进行存储,栈快,但是大小受限,我对于那些小点的数据且大小确定能存的我存,存不了的我放到堆里,我栈只存一个变量名和引用的映射关系,你要是吧,引用给你,去堆里找把。至于那些像多维数组一样的引用套娃行为我也只存储变量名个一个引用的映射,至于这个引用中存储的是不是另一个引用我就不确定了,你自己看着办吧!

3.6 new一个对象分成几步?

Person p = new Person() 执行new命令时程序执行两步:

a:在堆内存中开辟一段空间,存储new出 来的对象;

b:在栈内存中添加一个变量p,p中存放的是该对象在堆内存中开始存放处的物理地址

p = null;

执行此步骤的时候程序只是更改栈内存中的P变量所保存的地址,把地址指向null,

而并没有操作堆内存(把p所指向的对象实例清空回收)

3.7 由类创建一个对象,JVM 内存中发生了哪些事情?

MyClass mc = new MyClass();

以这条语句为例,new MyClass()则在堆中分配了对象mc成员变量的空间,MyClass mc =使虚拟机栈中生成了一个指向MyClass 实例化对象的地址。

4. 扩展考点

4.1 包装类和常量池

yan4

自动装箱操作

值类型自动转换成它对应的类类型-autoboxing、类类型自动转成它所对应的值类型-unboxing

装箱;
Integer a = 9; // Integer a = new Integer(9);
拆箱 int c = new Integer(9); // int c = new Integer(9).intValue(); // 9

缓存机制常量池

Java中Integer类中有一个缓存IntegerCache,在创建对象时如果缓存中已存在,则不会新建,默认缓存范围为[-128~127]。当使用new运算符是,则不会使用常量池中的对象。

基本数据类型中只有浮点数没有常量池

字符串缓冲池

yan4.Test2

先找如果有加把引用拿过来,没有在创建

new出来的字符串是对里边的东西,21行创建了两个对象

针对String类型变量的任意操作都会引发对象的新建

String s6 = "ab";
String s7 = s6 + "";
s6 == s7; // false

4.2 obj对象中有哪些常见的方法–目前

toString

直接输出一个对象时,实际上默认输出的是该对象toString()方法的返回值。所有的Java类中都有这个方 法,因为Java类都直接或者间接的继承于Object类,而Object类中定义了toString方法。为了实现用户 自定义输出格式,允许在类中覆盖定义toString方法

hashCode()

1).在Java应用程序程序执行期间,对于同一对象多次调用hashCode()方法时,其返回的哈希码是相同的,前提是将对象进行equals比较时所用的标尺信息未做修改。在Java应用程序的一次执行到另外一次执行,同一对象的hashCode()返回的哈希码无须保持一致;

2).如果两个对象相等(依据:调用equals()方法),那么这两个对象调用hashCode()返回的哈希码也必须相等;

3).反之,两个对象调用hasCode()返回的哈希码相等,这两个对象不一定相等。

哈希码(HashCode),并不是完全唯一的,它是一种算法,让同一个类的对象按照自己不同的特征尽量的有不同的哈希码,但不表示不同的对象哈希码完全不同。

哈希码的大小128 比特,也就是 16 字节;这个值得大小是固定的,但是文件或对象数据的大小可比这大得多,用一个比自己小的数据值怎么可能唯一的标识自己这个大小量级的所有数据。能唯一的标识自己的只有自己。

String s1 = new String("123456");
String s2 = new String("123456");
System.out.println(s1.equals(s2)); // true
System.out.println(s1 == s2); // false
System.out.println(s1.hashCode()); // 1450575459
System.out.println(s2.hashCode()); // 1450575459

new操作一定会在堆中创建新的对象,地址不同,值相同,哈希值相同。

equals()

Java中的约定:重写equals()方法必须重写hasCode()方法

hashCode()方法返回一个整形数值,表示该对象的哈希码值。

getClass方法用于获得当前对象的类型

特定运算符优先级

new > .

instanceof

判定一个对象是不是一个类型(的子类的实例化)

垃圾回收 finalize()

protected void finalize(); finalize方法主要与Java垃圾回收机制有关。

首先我们看一下finalized方 法在Object中的具体定义:

protected void finalize() throws Throwable { }

我们发现Object类中finalize方法被定义成一个空方法,为什么要如此定义呢?

finalize方法的调用时机 是怎么样的呢?

首先,Object中定义finalize方法表明Java中每一个对象都将具有finalize这种行为,其具体调用时机在: JVM准备对此对形象所占用的内存空间进行垃圾回收前,将被调用。

由此可以看出,此方法并不是由我 们主动去调用的(虽然可以主动去调用,此时与其他自定义方法无异)。


5. 多态

多态形成的三个方法:

  • 有继承,父类定义方法,子类重写方法
  • 父类的引用指向子类的对象
  • 可以使用参数传递时多态,也可以直接创建对象时多态

com.yan5

多态

  • 传入不同的参数,然后方法内部自行判定处理逻辑。(方法重载)

  • 父类 变量名 = new 子类();new谁就是谁

5.1 参数多态

public class Test01 {
	public static void main(String[] args) {
		Fa cc = new Fa();
		//调用的是一个名称为pp的方法,但是参数不同执行的处理不同
		cc.pp();// Fa...pp
		cc.pp(12);// Fa.pp(int)
	}
}

class Fa {
	public void pp() {
		System.out.println("Fa...pp");
	}

	public void pp(int k) {
		System.out.println("Fa.pp(int)");
	}
}

5.2 继承多态

数据类型有俩,变量想调用子类的方法时编译会报错(可用强制类型转换还解决)

  • 编译期类型:编译器所识别的类型,及声明类型
  • 运行时类型:及new的类型

反正这里就是各种各样的麻烦,属性重写很破烦,不作死就不会死,不要给自己挖坑

向下面这种声明父类、new子类的挖坑行为显然也是有规律的,

属性的话是向着父类找,但是方法是向着子类找。

package com.多态;

public class 属性的覆盖 {
	public static void main(String[] args) {
		A1 ab = new B1();
		B1 bc = new C1();
		A1 ac = new C1();
		ab.getI(); // B1
		bc.getI(); // C1
		ac.getI(); // C1
		System.out.println(ab.i); // A1
		System.out.println(ac.i); // A1
		System.out.println(bc.i); // B1
	}
}

class A1 {
	public String i = "A1";

	public void getI() {
		System.out.println("A1");
	}
}

class B1 extends A1 {
	public String i = "B1";

	public void getI() {
		System.out.println("B1");
	}
}

class C1 extends B1 {
	public String i = "C1";

	public void getI() {
		System.out.println("C1");
	}
}

5.3 方法的覆盖

添加注解@Override可以进行编译检查,可以找到自己覆盖的方错在哪里

要求方法的签名一致

  • 方法名一致
  • 参数类型必须一致
  • 范围限定词:子类>=父类
  • 所抛出的异常:父类>=子类
  • 返回值类型:子类<=父类

5.4 当前对象this关键字

构造函数中的构造函数this()

this();调用当前类中的另外一个构造器,当然是可以传参的,不要调自己。而且还必须要在第一行。

构造函数调用必须是构造函数中的第一条语句。

那就是说只能在构造方法中使用构造方法,且要在第一行

当参数名与形参名重名时

this代表当前对象,可以用this.属性名来标识属性

5.6 属性的覆盖(待整理)

属性的覆盖会导致二义性,一般不用属性的覆盖

super表示父类对象,想访问父类中被覆盖的属性或者方法时应用。

5.7 构造器的调用

没有构造器时默认会调用无参构造器,子类构造器第一行会先调用父类的无参构造器。

一个构造器方法的第一句一定是super()父类的构造器,你不写它会默认调用无参的父类构造器super()


扩展小芝士

  • 数据类型有俩

    • 编译期类型:编译器所识别的类型,及声明类型
    • 运行时类型:及new的类型
  • 在方法中间可以抛出异常

  • 子类中表示自己关键字this,表示父类super

  • 为什么区分一个方法用的是方法名和参数类型列表,你要用一个方法要啥东西,要方法名把,要参数把,返回值似乎没必要,所以区分一个方法用的就是方法名的形参类型列表。

  • 没有构造器时默认会调用无参构造器,子类构造器第一行会调用父类的无参构造器。

  • null是java中一个特殊的空间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值