2_数组_面向对象_三大特性_三个修饰符

JavaSE_第二周

安装eclipse

创建工程:
	创建工作空间:workspase
	启动快捷键:指定workspase
	工程名称
	finish结束
设置编码:window -> preference -> general -> workspace,设置utf-8

面板的设置:
	package explorer|project explorer
	outline:查看类结构(方法...)
	console:结果显示的面板
创建包:
	包名命名使用反向域名
	在src根下创建包
创建类:
	在指定的包下创建类
	可同时勾选main方法
	运行:run as -> java application

eclipse快捷键

 整行删除:ctrl + D

alt + 上下箭头: 向上或向下移动代码

ctrl + alt + 上下箭头:向上或向下复制代码

syso或sout->alt+/提示快捷键

自动导包:ctrl+shift+o

数组

概念:

​ 可以同时存储多个相同类型的数据

特点:

​ 固定长度,元素的数据类型统一

定义:
1)数据类型[] 数组名;
	数组名 = new 数据类型[长度]
2)数据类型[] 数组名 = new 数据类型[长度]
3)数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3...}
4)数据类型[] 数组名 = {元素1,元素2,元素3...}

声明时也可以把中括号放到数组名后面,例如:数据类型 数组名[] = new 数据类型[长度]
访问:
赋值和取值
数组名[下标] = 值;
有效下标范围:0~数组长度-1
访问无效下标,会导致数组下标越界异常(java.lang.ArrayIndexOutOfBoudsException)

数组的长度:length属性

数组的遍历
//普通for循环
public class TestArray2{
	public static void main(String[] args){
		
		int[] numbers = new int[5];
		
		//赋值
		numbers[0] = 111;
		numbers[1] = 222;
		numbers[2] = 333;
		numbers[3] = 444;
		numbers[4] = 555;
		
		//遍历
		for(int i = 0; i < numbers.length; i++){
			System.out.println(numbers[i]);
		}
	}
}
//增强for循环遍历数组
for(数据类型 变量 : 数组){
	//循环每次都将元素赋值给该变量
}
数组元素的默认值
public class TestArray3{
	public static void main(String[] args){
		/*
            整型:0

            浮点型:0.0

            布尔类型:false

            字符类型:"\u0000" (显示时显示空白)

            String型:null空值

            注:null不能使用,否则会造成空指针异常(java.lang.NullPointException)
		*/
		int[] nums = new int[4];
		
		for(int i = 0; i < nums.length; i++){
			System.out.println(nums[i]);//0
		}
		
		double[] scores = new double[4];
		
		for(int i = 0; i < scores.length; i++){
			System.out.println(scores[i]);//0.0
		}
		
		boolean[] bools = new boolean[4];
		
		for(int i = 0; i < bools.length; i++){
			System.out.println(bools[i]);//false
		}
		
		//字符默认值:\u0000  显示时显示一个空白
		char[] cs = new char[3];
		for(int i = 0; i < cs.length; i++) {
			System.out.println(cs[i]);
		}
		
		String[] names = new String[5];
		
		names[0] = "Tom";
		names[1] = "Jack";
		names[2] = "Amy";
		names[3] = "marry";
		
		for(int i = 0; i < names.length; i++){
			System.out.println(names[i]);//null
		}
	}
}
数组的扩容

创建数组时,必须显示指定长度,在创建之后不可更改长度

扩容的思路:

​ 创建大于原数组长度的新数组

​ 将原数组中的元素依次复制到新数组中

复制的方式:
	1)循环将原数组中所有元素逐一赋值给新数组;

	2)System.arraycopy(原数组,原数组起始,新数组,新数组起始,长度);

	3)java.util.Arrays.copyOf(原数组,新长度);//返回带有原值的新数组
地址的替换

数组作为引用类型之一,其变量中存储的是数组的地址

完成元素复制后,需将新数组地址赋值给原变量进行替换

public class TestCopyArray{
	public static void main(String[] args){
		
		int[] nums = new int[5];
		
		nums[0] = 11;
		nums[1] = 22;
		nums[2] = 33;
		nums[3] = 44;
		nums[4] = 55;
		
		//1.创建比原数组大的新数组
		int[] newNums = new int[nums.length*2];
		//2.复制原数组中的所有数据到新数组中
		for(int i = 0; i < nums.length; i++){
			newNums[i] = nums[i];
		}
		//遍历原数组
		for(int i = 0; i < nums.length; i++){
			System.out.print(nums[i] + "\t");
		}
		System.out.println();
		//遍历新数组
		for(int i = 0; i < newNums.length; i++){
			System.out.print(newNums[i] + "\t");
		}
	}
}
public class TestCopyArray2{
	public static void main(String[] args){
		
		int[] nums = new int[5];
		
		nums[0] = 11;
		nums[1] = 22;
		nums[2] = 33;
		nums[3] = 44;
		nums[4] = 55;
		
		//复制
		
		//1.创建新数组
		int[] newNums = new int[nums.length*2];
		
		//2.使用System.arraycopy(原数组,原数组起始,新数组,新数组起始,长度)
		System.arraycopy(nums,0,newNums,0,nums.length);
		
		//遍历新数组
		for(int i = 0; i < newNums.length; i++){
			System.out.print(newNums[i] + "\t");
		}
	}
}
//import java.util.Arrayss;
public class TestCopyArray3{
	public static void main(String[] args){
		
		int[] nums = new int[5];
		
		nums[0] = 11;
		nums[1] = 22;
		nums[2] = 33;
		nums[3] = 44;
		nums[4] = 55;
		
		//1.创建新数组,复制元素
		// java.util.Arrays.copyOf(原数组,新长度)
		int[] newNums = java.util.Arrays.copyOf(nums,nums.length*2);//将带有原值的新数组返回给我
		
		//2.遍历
		for(int i = 0; i < newNums.length; i++){
			System.out.print(newNums[i] + "\t");
		}
	}
}
数组类型的参数和返回值

传递参数时,基本数据类型传递的是变量中的值;引用数据类型传递的是变量中的地址

public class TestCompare{
	
	public static void main(String[] args){
		
		//1.基本数据类型的传递,是“值”的传递,一方改变,不会影响另一方
		//2.引用数据类型的传递,是“地址”的传递,一方改变,会影响另一方
		
		int i = 10;
		m1(i);
		System.out.println(i);//10
		
		byte b = 123;
		b = m2(b);
		System.out.println(b);//124
		
		int[] nums = {1,2,3,4};
		m3(nums);
		System.out.println(nums[0] + "\t" + nums[1] + "\t" + nums[2] + "\t" + nums[3]);//688...
		
		int[] numbers = {5,4,3,2,1};
		m4(numbers);
		System.out.println(numbers.length);//没有变
		
		int[] arrays = {111,222,333};
		
		arrays = m5(arrays);
		
		System.out.println(arrays.length);//6
	}
	
	public static void m1(int n){
		n =  n * 2;
		System.out.println(n);//20
	}
	
	public static byte m2(byte c){
		c++;
		return c;
	}
	
	public static void m3(int[] arr){
		arr[0] = 688;
	}
	
	public static void m4(int[] arr){
		int[] numbers = java.util.Arrays.copyOf(arr,arr.length*2);
	}
	
	public static int[] m5(int[] arr){
		int[] newNums = new int[arr.length*2];
		System.arraycopy(arr,0, newNums,0,arr.length);
		return newNums;
	}
}

调用数组类型返回值的方法时,方法执行后,返回的是数组的地址

排序

冒泡排序:两两相邻位置的元素相比较,如果顺序不符合,则交换两位置的元素

public class TestBubble{
	
	public static void main(String[] args){
		
		int[] nums = {4,3,5,2,1};
		
		//冒泡排序:相邻的两个数值比较大小,互换位置
		
		//轮次:数组长度 - 1
		//单轮次数:(数组长度 - 1)基础上做逐级递减
		
		//外层:length-1; 内层:length -1 -i;
		
		
		for(int i = 0; i < nums.length - 1; i++){//外层循环:控制比较轮次(nums.length -1)
			for(int j = 0; j < nums.length - 1 - i; j++){//内层循环:单轮当中的比较次数
				if(nums[j] > nums[j + 1]){
					int temp = nums[j];
					nums[j] = nums[j + 1];
					nums[j + 1] = temp;
				}
			}
			for(int k = 0; k < nums.length; k++){
				System.out.print(nums[k] + "\t");
			}
			System.out.println();
		}

	}
}

选择排序:固定值与其他值依次比较大小,互换位置

public class TestSelect{
	
	public static void main(String[] args){
		//选择排序
		//固定值与其他值依次比较,互换位置
		int[] nums = {4,3,5,2,1};
		
		for(int i = 0; i < nums.length - 1; i++){
			for(int j = i + 1; j < nums.length; j++){
				if(nums[i] > nums[j]){
					int temp = nums[i];
					nums[i] = nums[j];
					nums[j] = temp;
				}
			}
			
			for(int k = 0; k < nums.length; k++){
				System.out.print(nums[k] + "\t");
			}
			System.out.println();
		}
	}
}

JDK排序:java.util.Arrays.sort(数组名); //jdk提供(升序)

public class TestJDKSort{
	
	public static void main(String[] args){
		
		int[] nums = {4,3,5,2,1};
		
		
		java.util.Arrays.sort(nums);//默认升序
		
		//手工完成元素的倒置
		for(int i = 0; i < nums.length / 2; i++){
			int temp = nums[i];
			nums[i] = nums[nums.length - 1 - i];
			nums[nums.length - 1 - i] = temp;
		}
		
		for(int k = 0; k < nums.length; k++){
			System.out.print(nums[k] + "\t");
		}
		System.out.println();
	}
}
可变长参数

概念:可接受多个同类型的实参,个数不限,使用方式与数组

语法:数据类型…形参名//必须定义在形参列表的最后,且只能有一个

可变长参数一般作为数组使用

public class TestChangeLength{
	
	public static void main(String[] args){
		
		int[] numbers = {1,2,3,4,5};
		//1.支持传递数组类型的实参
//		method(numbers);
		//2.支持传递零散数据的实参
//		method(5,6,7,8,9);//也支持0个参数
		
		
		int[] newArray = expand(10,numbers);
		method(newArray);
	}
	
	//函数的参数是"可变长参数"
	public static void method(int... arr){
		System.out.println("---method executed---");
		for(int i = 0; i < arr.length; i++){
			System.out.print(arr[i] + "\t");
		}
		System.out.println();
	}
	
	public static int[] expand(int length, int... arr){// 规则:必须在形参列表最后,且只能有一个,支持0~N个参数
		
		int[] newNums = new int[length];
		
		System.arraycopy(arr,0,newNums,0,arr.length);
		
		return newNums;
	}
}

二维数组

概念:

​ 一维数组中的一维数组;数组中的元素还是数组

二维数组的内存分配

高维数组中的每一个元素保存了低维数组的地址,访问array[0]等价于访问0x0000A111

二维数组的赋值与访问
public class Test2DArray{
	
	public static void main(String[] args){
		
		int[][] nums = new int[3][5];
		
		nums[0][0] = 10;
		nums[0][3] = 20;
		nums[1][2] = 30;
		nums[1][4] = 40;
		nums[2][1] = 50;
		nums[2][3] = 60;
		
		for(int i = 0; i < nums.length; i++){//外层控制行数
			for(int j = 0; j < nums[i].length; j++){
				System.out.print(nums[i][j] + "\t");
			}
			System.out.println();
		}
		
		System.out.println(nums.length);//nums变量中,所持有的地址对应的数组长度为多少
		System.out.println(nums[0].length);//nums[0] 高维数组中的第一个元素所指向的低维数组的长度
		
		//高维数组中的每一个元素,保存了低维数组的地址,访问array[0]相当于访问0x0000A001
		System.out.println(nums[0]);
		System.out.println(nums[1]);
		System.out.println(nums[2]);
		
	}
}

junit单元测试

定义方法:公有无参无返回的方法

添加注解:@Test

引入junit4环境:ctrl+1 -> add libary添加环境依赖

执行:run as --> junit test

如果需要断言结果时:Assert.assertEquals(期望值,实际值);

package com.qf.test;

import java.util.Arrays;
import org.junit.Assert;
import org.junit.Test;
import com.qf.demo.Demo1;

public class Test1 {
	@Test
	public void test1(){
		int[] nums = {43,23,66,56,7,89,32};
		System.out.println("最小值是:" + Demo1.minNum(nums));
		//使用现有的工具类
		System.out.println(Arrays.toString(nums));
		//如果需要断言结果时:Assert.arrertEquals(期望值,实际值);
		Assert.assertEquals(6, Demo1.minNum(nums));
	}
}

debug运行

打断点:在需要监控的行首打断点

运行模式:debug模式,debug as --> junit test

F5 :进入方法

F6:执行下一句

F7:跳出方法

F8:执行下一个断点,如果没有下一个段断点,则执行结束

面向对象

面向对象思想:
面向对象思想:一切客观存在的事物都是对象,万物皆对象
			任何对象,一定具有自己的特征和行为

特征:称为属性,一般为名词,代表对象有什么

行为:称为方法,一般为动词,代表对象能做什么

类的抽取:在现实中的一组相同或类似的对象中,提取共性的特征和行为,保存在程序中的模板里

类的定义
类由属性和方法构成

属性:field、全局变量、字段、实例成员属性;

			位置:定义在类内,方法外;

			语法:数据类型 属性;

			注:都有默认值(0/0.0/false/null)

方法:method、实例方法

		public 返回类型 方法名(参数列表){//方法体}
对象的创建

创建对象:实例化过程;类-抽象概念;对象-具体概念;一个类可以创建无数个对象;
	使用new关键字创建对象---在内存中开辟内存空间;由引用变量指向内存空间;

例:User u=new User();  此时u就是引用变量,new User()代表内存中开辟空间;u存的是地址
类与对象的关系

类:定义了对象应具有的特征和行为,类是对象的模板

对象:拥有多个特征和行为的实体,对象是类的实例

实例变量与局部变量的区别
				局部变量				全局变量(实例变量)
定义位置		方法或方法内的结构当中		类的内部,方法的外部
默认值				无默认值				字面值(与数组相同)
使用范围		从定义行到包含其结构结束		本类有效
命名冲突		不允许与局部变量重名		不允许与实例变量重名,可与局部变量重名;局部变量优先

public class Demo2 {

	public static void main(String[] args) {
		int[] a = {1,2,3,4,5};
		expand(a); 
		changeArray(a); 
		printArray(a); // 10 2 3 4 5
	}
	public static void expand(int[] a){ //a 是局部变量
		int[] newArray = new int[a.length * 2]; 
		System.arraycopy(a, 0, newArray, 0, a.length); 
		a = newArray; //局部变量在方法结束后销毁了
	} 
	public static void changeArray(int[] a){ 
		a[0] = 10; 
	} 
	public static void printArray(int[] a){ 
		for(int i = 0; i<a.length; i++){ 
			System.out.print(a[i] + "\t"); 
		} 
		System.out.println(); 
	}
}
方法的重载
方法重载: 在一个类中定义多个相同名称的方法
要求: 
	方法名称相同
	参数列表不同(类型,个数,顺序)
	与访问修饰符,返回值无关
好处:
	 屏蔽用户的使用差异,方便
构造方法(构造器)
构造方法:类中的特殊方法,主要用于创建对象
	特点:
		名称与类名完全相同(包括大小写)
		没有返回值类型
		创建对象时(new对象时),触发构造方法的调用,不可通过句点手工调用
	【注】如果没有在类中显示定义构造方法,则编译器默认提供无参构造方法
 		如果已经手动添加过有参构造方法,则无参构造方法不再默认提供,结合需求,自行添加(建议:手动添加			无参构造方法)
对象创建过程
Student student=new Student();  //调用无参构造器,并且student引用变量指向了内存地址;

new Student() 触发对象的创建
对象的创建过程:
	内存中开辟对象空间
	为各个属性赋予初始值
	执行构造方法中的代码
	[将对象的地址赋值给变量]
构造方法的重载

​ 构造方法也可以重载,遵循重载的原则

class Student{
	String age;
 	Student(){
     
 	}
 
 	Student(String name){
     	//重载的构造方法
     	//给同类型属性赋值
 	}
}
this关键字
this 代表" 当前实例 ", 既是模板中的当前对象,模板服务于哪个对象,this就指向那个对象 

this关键字:

	第一种用法:调用本类中的实例属性,实例方法,例如:this.name,this.run();

	第二种用法:调用本类中的其他构造方法,如:this(),this(实参)

注:this 用于区分局部变量与全局变量同名的情况;此时全局变量前的this不能省;

	this([实参])	必须在构造方法首行,仅可在构造方法中,不能在普通方法中
public class T{
 	int age=10;
 	public void test(){
    	int age=20;
    	Sytem.out.println(age);//20  局部变量优先
    	Sytem.out.println(this.age);//10 用this指代的是全局变量  
 	}
 }

面向对象三大特性

封装

概念:

​ 尽可能隐藏对象的内部实现细节,控制对象的修改及访问的权限

访问修饰符

​ private可将属性修饰为私有,可达到本类可见的效果

公共访问方法

​ get/set方法是外界访问私有属性的唯一渠道,方法内部可对数据进行过滤

​ (可在set方法中添加过滤条件)

​ 提供公共访问方法,以保证数据正常录入。

public class TestEncapsulation {	
	public static void main(String[] args) {
		Student s1 = new Student();
		s1.name = "Tom";
		s1.setAge(20);//调用方法完成赋值操作
		s1.sex = "male";
		s1.score = 99.0;
		
		System.out.println(s1.getAge());//调用方法完成取值操作
	}

}

class Student{
	
	String name;//属性也称字段
	private int age;//私有属性,本类可见,其他位置不可见
	String sex;
	double score;
	
	public Student() {}
	
	/**
	 * 为age 属性赋值的方法
	 * */
	public void setAge(int age) {
		if(age > 0 && age < 153) {//合法区间
			this.age = age;
		}else {
			this.age = 18;//录入非法数据时的默认值
		}
	}
	
	/**
	 * 为age属性取值的方法
	 * 
	 * */
	public int getAge() {
		return this.age;
	}
	
}
退出for嵌套switch
//当for中嵌套switch语句,可直接退出的方法
outer:for(int i = 0; i < 5; i++) {
	System.out.println("当前值:" + i);
	switch(i) {
		case 3:
			System.out.println("满足条件,退出");
			break outer;
	}
}

继承

程序中的继承
程序中的继承是类与类之间的特征和行为的一种赠与与获得

类与类之间必须满足“is a”的关系
父类的选择
父类的选择:功能越精细,重合点越多,越接近直接父类
父类的抽象
父类的抽象:根据程序需要使用到的多个具体类,进行共性的抽取,进而定义父类

在一组相同或类似的类中,抽取出共性的特征和行为,定义在父类中,实现重用
语法
class 子类 extends 父类{}//定义子类时,显示继承父类
应用
产生继承关系后,子类可以使用父类中的属性和方法,也可定义子类独有的属性和方法
好处

既提高代码复用性,又提高代码的可扩展性

特点
Java 为单继承,一个子类只能有一个直接父类,但可以多级继承,属性和方法逐级叠加
不可继承
父类的构造方法,子类不可继承
父类有private修饰的属性和方法,不可继承(不可见)
父子类由default修饰的属性和方法,子类不在同一包时不可继承(不可见)

	子类可以**调用**父类的属性和方法、构造器;使用的是super关键字
	子类**继承**父类的属性和方法,**不能继承**构造方法;	
	子类还可以将父类方法进行**重写**
	子类继承父类的非私有成员,至于调用时需要看属性是默认还是protected
	子类构造器中第一句话**默认是super()**,如果父类没有无参构造器,此时需要由子类调用现在的父类带参构造器(根据实参列表去匹配)
Super关键字
当父类的属性或方法存在重名(属性遮蔽,方法覆盖)时,可用super加以区分

super关键字

	第一种用法:在子类中,可以通过“super.”的形式访问父类的属性和方法,可以解决一定的属性遮蔽,方法	覆盖后的父类成员调用问题

	第二种用法:super()表示在子类构造方法的首行,调用父类的无参构造方法(默认隐式存在)

注:子类构造器中第一句话默认是super();
		如果父类中没有无参构造器,则需要在子类构造器中第一句话调用现有构造器

class Father{
 int age=20;
 public void test(){
     
 }
 public Father(int age){
     
 }
}
class Son extends Father{
 int age=10;
 public Son(){
     super(age);
 }
 
  public void test(){
     
 }
 public void test1(){
 	System.out.println(age);//子类属性
     System.out.println(this.age);//子类属性
     System.out.println(super.age);//父类属性
 }
  public void test2(){
  	int age=5;
 	System.out.println(age);//局部变性
     System.out.println(this.age);//子类属性
     System.out.println(super.age);//父类属性
 }
 public void test3(){
     test();
 }
}
this与super
this 和 super 使用在方法中时,都要求在首行

	当子类构造中使用了this() 或this(实参) ,即不再同时书写super() 或super(实参),会由this()指向的构造方法完成super()的调用
访问修饰符
private:只能限于本类内使用

默认:限于同包内使用

protected:限于跨包后的子类中使用

public:没有限制,处处可用;

注:修饰类时只能使用public和默认;
	四个修饰符都可以应用到属性、方法、构造方法的定义中
方法的重写
方法的覆盖/重写(override):

		当父类提供的方法无法满足子类需求时,可以在子类中定义与父类相同的方法进行覆盖

原则:

		方法名称,参数列表,返回值类型必须与父类相同

		访问修饰符可与父类相同或比父类更宽泛

执行机制:子类覆盖父类方法后,优先执行子类覆盖后的版本
public class TestOverride {

	public static void main(String[] args) {

		Dog dog1 = new Dog();
		dog1.eat();//覆盖后,优先执行子类覆盖后的版本
		dog1.sleep();
		
		Cat cat1 = new Cat();
		cat1.eat();
	}

}

class Animal{
	
	String breed;
	int age;
	String sex;
	
	public void eat() {
		System.out.println("动物在吃...");
	}
	
	void sleep() {
		System.out.println("动物在睡...");
	}
	
}

class Dog extends Animal{
	
	String furColor;
	
	//在子类中定义与父类相同的方法进行覆盖
	public void eat() {
		System.out.println("狗在吃骨头");
	}
	
	public void sleep() {
		System.out.println("狗在睡...");
	}
	
	public void run() {}
}

class Cat extends Animal{
	
//	public void eat() {}
}
继承关系下的对象创建

继承关系下,构建子类对象时,会先构建父类对象;

由"父类共性" + '“子类独有” 组合成一个完整的子类对象

继承关系下的对象创建流程

1.构建父类对象

2.初始化自身属性

3.执行自身构造方法中的逻辑代码

public class TestCreateSort {

	public static void main(String[] args) {

		new C();//field1	field2	field3
		
	}

}

class A{//extends Object
	
	String field1 = "A的属性";//1.初始化属性
	
	public A() {
		System.out.println("A的构造方法被执行  " + field1);//2.执行构造方法中的逻辑代码
	}
}

class B extends A{
	String field2 = "B的属性";//2.初始化属性
	
	public B() {
		super();//1.调用父类的构造方法(默认调用父类无参的构造方法)隐式存在
		System.out.println("B的构造方法被执行  " + field2);//3.执行构造方法中的逻辑代码
	}
	
}

class C extends B{
	String field3 = "C的属性";//2.初始化属性
	
	public C() {
		super();//1.调用父类的构造方法(默认调用父类无参的构造方法)隐式存在
		System.out.println("C的构造方法被执行  " + field3);//3.执行构造方法中的逻辑代码
	}
	
}

多态

概念
一种事物多种不同的表现形态,

父类引用指向子类对象,从而产生多种形态

构成多态的前提:二者具有直接或间接的继承关系时,父类引用指向子类对象,即形成多态

父类引用仅可调用父类所声明的属性和方法,不可调用子类独有的属性和方法
多态中的方法重写

思考:如果子类重写了父类中的方法,以父类类型引用调用此方法时,优先执行父类中的方法还是子类中的方法?

实际运行过程中,依旧遵循重写原则,如果子类重写了父类中的方法,执行子类中重写后的方法,否则执行父类中的方法

多态中的成员访问特点
	1)成员变量----->编译看左边,运行看左边     Fu  f = new Zi() ;   向上转型
										输出:f.num
										
	2)成员方法----->编译看左边,运行看右边   ----存在方法重写 
									    输出:f.show() ;
	3)静态的成员方法----->静态方法算不上方法重写,静态的东西是直接和类相关的,随着类的加载而加载
					优先于对象存在! 推荐访问:变量/方法---(静态)----类名.变量名/方法名()
					
     4)构造方法------->对数据进行初始化
     	   存在继承关系,分层初始化----  先让父类进行初始化,然后再是子类进行初始化!
多态的应用
方法重载可以解决接受不同对象参数的问题,但缺点也比较明显。

首先,随着子类的增加,类需要继续提供大量的方法重载,多次修改并重新编译源文件,其次,每一个方法与某一种具体类型形成了密不可分的关系,耦合太高

场景一:使用父类作为方法形参实现多态,使方法参数的类型更为广泛

场景二:使用父类作为方法返回值实现多态,使方法可以返回不同子类对象

作用:屏蔽子类间的差异,灵活,耦合度低
多态的好处和弊端
多态的好处:提高了代码的维护性,提高了代码的扩展性。

弊端:不能使用子类中的特有功能

解决方案:

1)具体类(子类) 创建具体类 ----
    Cat c = new Cat() ;
内存在中---又在创建一个新的对象,开销资源太大,占用内存空间!
2)将父类的引用强制子类的引用(符号强制类型转换的语法)
Cat c2 = (Cat)a ; (推荐这种方式)
向上转型(装箱)
向上转型(装箱):父类引用中保存真实子类对象成为向上转型(即多态核心概念)

子类转为父类---自动转换

注:仅可调用父类中所声明的属性和方法
向下转型(拆箱)
向下转型(拆箱) :将父类引用中的真实子类对象强转回子类本身类型,称为向下转型

父类转为子类---强制转换

注:只有转换回子类真实类型,才可调用子类独有的属性和方法
类转换异常

向下转型时,如果父类引用中的子类对象的类型与目标类型不匹配,则会发生类型异常 java.lang.ClassCastException

instanceof关键字
向下转型时应判断引用中的对象的真实类型,保证类型转换的正确性

语法:父类引用	instanceof 类型 //返回boolean 类型结果

含义:判断引用变量是否是后者类型的一个实例,返回boolean;
package com.qf.day14.t3.polymorphic;


/**
 * 		多态两种应用场景:
 * 			场景一:使用父类作为方法形参,实现多态,使方法类型的参数更为宽泛
 * 			
 * 			场景二: 使用父类作为方法返回值实现多态,使方法可以返回不同子类对象
 * 		多态的作用:
 * 			屏蔽子类间的差异,灵活,耦合度低
 * 
 * */
public class TestApplyPolymorphic {
/**
 * 	向上转型(装箱):父类引用中保存真实子类对象成为向上转型(即多态核心概念)
 *		
 *		注:仅可调用父类中所声明的属性和方法
 *	
 *	向下转型(拆箱) :将父类引用中的真实子类对象强转回子类本身类型,称为向下转型
 *		
 *		注:只有转换回子类真实类型,才可调用子类独有的属性和方法
 *		
 *		向下转型时,如果父类引用中的子类对象的类型与目标类型不匹配,则会发生类型异常		java.lang.ClassCastException
 */
	public static void main(String[] args) {

		Car car =  new Car();//自身类型引用指向自身类型对象
		car.type = "小汽车";
		car.speed = 120;
		car.price = 300000;
		car.brand = "BMW";//独有属性
		
		//car.run();
		
		Vehicle veh = new Car();//父类引用指向子类对象
		veh.type = "小汽车";
		veh.speed = 100;
		veh.price = 200000;
		
		//veh.run();//多态场景下,父类引用调用方法,如果被子类覆盖过,优先执行子类覆盖后的版本
		
		Bus bus = new Bus();
		bus.type = "公交车";
		bus.speed = 40;
		bus.price = 1000000.0;
		bus.seatNum = 24;
		
		Bicycle bic = new Bicycle();
		bic.type = "自行车";
		bic.price = 2000.0;
		bic.speed = 20;
		bic.color = "红色";
		
		Employee emp = new Employee();
		emp.name = "Tom";
		emp.goHome(bic);
		emp.goHome(car);
		emp.goHome(bus);
		
		//调用员工买车的方法(新)
		Employee emp2 = new Employee();
		emp2.name = "jack";
		Vehicle myVehicle = emp2.buyVehicle(50);
		if(myVehicle != null) {
			emp2.goHome(myVehicle);	
		}else {
			System.out.println("钱不够,没买成...-_-");
		}
		
		/**
		 * 	向下转型时应判断引用中的对象的真实类型,保证类型转换的正确性
		 * 	
		 * 	instanceof 关键字	
		 * 		
		 * 		语法:父类引用	instanceof 类型 //返回boolean 类型结果
		 */
		
		//拆箱:将父类引用中的真实子类对象,转回其本身类型
		if(myVehicle instanceof Car) {//判断myVehicle引用中的真实子类对象,是否为Car类型
			Car myCar = (Car)myVehicle;
			System.out.println(myCar.brand);	
		}else if(myVehicle instanceof Bus) {
			Bus myBus = (Bus)myVehicle;
			System.out.println(myBus.seatNum);
		}else if(myVehicle instanceof Bicycle) {
			Bicycle myBicycle = (Bicycle)myVehicle;
			System.out.println(myBicycle.color);
		}
		
	}

}


//员工
class Employee{
	
	String name;
	
	//回家(父类类型作为方法形参,实现多态)
	public void goHome(Vehicle veh) {
		System.out.print(name + "正在乘坐");
		veh.run();
	}
	
	/*public void goHome(Bus bus) {
		System.out.print(name + "正在乘坐");
		bus.run();
	}
	public void goHome(Bicycle bic) {//藕合高(模块与模块之间的关联程度)
		System.out.print(name + "正在乘坐");
		bic.run();
	}
	public void goHome(Car car) {
		System.out.print(name + "正在乘坐");
		car.run();
	}*/
	
	//员工买车(新)
	public Vehicle buyVehicle(int money) {//单位:万
		
		Vehicle veh = null;//方法返回值
		
		if(money > 100) {
			Bus bus = new Bus();
			bus.type = "公交车";
			bus.speed = 50;
			bus.price = 1100000;
			bus.seatNum = 24;
			veh = bus;
		}else if(money > 30) {
			Car car = new Car();
			car.type = "小汽车";
			car.speed = 125;
			car.price = 301000;
			car.brand = "Benz";
			veh = car;
		}else if(money > 1){
			Bicycle bic = new Bicycle();
			bic.type = "自行车";
			bic.speed = 25;
			bic.color = "红色";
			bic.price = 2100;
			veh = bic;
		}
		
		return veh;
	}
	
}



//交通工具类
class Vehicle{
	
	String type;
	int speed;
	double price;
	
	public void run() {
		System.out.println("一辆价值" + price + "RMB的" + type + "正以" + speed + "km/h的速度前进...");
	}
}

class Car extends Vehicle{
	
	String brand;
	
	public void run() {
		System.out.println("一辆价值" + price + "RMB的" + brand + "品牌的" + type + "正以" + speed + "km/h的速度前进...");
	}
}

class Bus extends Vehicle{
	
	int seatNum;
	
	public void run() {
		System.out.println("一辆价值" + price + "RMB的" + seatNum + "个座位的" + type + "正以" + speed + "km/h的速度前进...");
	}
}

class Bicycle extends Vehicle{
	
	String color;
	
	public void run() {
		System.out.println("一辆价值" + price + "RMB的" + color + "的" + type + "正以" + speed + "km/h的速度前进...");
	}
	
}

三个修饰符

abstract

abstract 的意思:抽象的,似是而非的,像却又不是,具备某种对象的特征,但不完整

抽象类
abstract 修饰类,意为 “不够完整,不够具体,不该独立存在”

抽象类的作用:

	1.可被子类继承,提供共性的属性和方法

	2.可声明为引用,可更纯粹的使用多态

抽象类构造方法的作用:

	构建子类对象时,先构建父类对象(父类共性 + 子类独有 = 完整的子类对象)

abstract 修饰类:不能new对象,但可以声明引用
抽象方法

abstract 修饰方法:只有方法声明,没有方法实现(需包含在抽象类中)

注:抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类

子类继承抽象类后,必须覆盖父类所有的抽象方法,否则子类还是抽象类

public class TestAbstract {

	public static void main(String[] args) {

//		new Animal();//不该被创建成实例独立存在
		
		Animal a = new Dog();//构建子类对象时,先构建父类对象(父类共性 + 子类独有 = 完整的子类对象)
		a.eat();
		
		
	}

}

abstract class Animal{//使用abstract修饰为抽象类,不能new对象(不能独立存在)含义:不够完整,不够具体,不该独立存在
	
	String breed;
	int age;
	String sex;
	
	public Animal() {
		System.out.println("---Animal() Executed---");
	}
	
	//抽象方法(只有方法声明 ,没有方法实现,意为不够完整不够具体),强制子类必须实现该抽象方法,提供完整的具体的版本
	public abstract void eat();
	
	public void sleep() {
		System.out.println("动物在睡...");
	}
}

class Dog extends Animal{//抽象类	 可以被子类继承,可以为子类提供共性
	
	public Dog() {
		super();
		System.out.println("---Dog() Executed---");
	}

	@Override
	public void eat() {
		System.out.println("狗在吃骨头...");
	}
	
}

static

静态和实例的区别

实例属性是每个对象各自持有的独立空间(多份) ,对象单方面的修改,不会影响其他对象

静态属性是整个类共同持有的共享空间(一份) ,任何对象修改,都会影响其他对象

Demo d=new Demo();
d在栈内存区;
new Demo()在堆内存区;并且每创建一次对象,就会在堆内存区中有一个地址;这个地址就是该对象的属性和方法等成员
类内的static属性方法在方法区;属于类,而不是某一个对象,所有对象共享该区的属性和方法
静态的概念
static 可以修饰属性和方法,即为静态属性(类属性)和静态方法(类方法)

静态成员是全类所有对象共享的,全类只有一份,不因创建多个对象而改变

不必创建对象,也可通过类名,直接访问静态成员

经验:访问静态属性和方法时,可直接通过“类名.属性名” 和“类名.方法名”(推荐)
静态的特点
I. 静态方法允许直接访问静态成员

II. 静态方法不能直接访问非静态成员

III. 静态方法不允许使用this和super关键字

IV. 静态方法可以继承,不能覆盖,没有多态
类加载
概念

JVM首次使用某个类时,将该类的.class文件加载到内存中,进行保存

加载时机
1).创建对象 

2).创建子类对象 

3).调用静态的属性和方法 

4).class.forName("全限定名"),
动态代码块
创建对象时,触发动态代码块的执行

执行地位:初始化属性之后,构造方法之前

作用:可为实例属性赋值,或必要的初始行为
静态代码块
类加载时触发代码块的执行(仅一次)

执行地位:静态属性初始化之后

作用:可为静态属性赋值,或必要的初始行为

final

最终类
final修饰类,该类不能被继承

如:String,Math,System均为final修饰的类,不能被继承
最终方法
final修饰方法:此方法不能被覆盖

意为最终方法,不支持子类以覆盖的形式修改
最终常量
final修饰变量:此变量值不能被修改--常量  	(无初始值,只允许赋值一次)

	1).局部变量:显示初始化

	2).实例常量不在提供默认值,必须手动赋予初始值

		实例常量的赋值:显示初始化,动态代码块,构造方法

			要求:

				a).实例常量赋值:在构造方法完成之前,为实例常量赋值即可

				b).如果在构造方法中为实例常量赋值,必须保证所有的构造方法度可正确赋值

	3).静态常量不在提供默认值,必须手动赋予初始值

		静态常量赋值:显示初始化,静态代码块

			要求:

				a).静态常量赋值:在类加载完成之前(通过类名调用之前),为静态常量赋值即可

不同常量类型的特点:

	基本数据类型常量:值不可变

	引用数据类型常量:地址不可变

properties文件的加载

创建java的Properties对象: Properties p=new Properties();

加载后缀为properties的文件:p.load(类名.class.getClassLoader().gerResourceAsStream("properties文件"));

读取文件中的名称对应的值:p.getProperty(properties文件中的)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值