Java温习——面向对象第二部分

一、深入变量

1 变量的分类和初始值

(1)分类

根据在类中定义位置的不同,变量分为成员变量和局部变量;

成员变量,又字段field(不要称为属性),直接定义在类中,各方法外,包括类成员变量(使用static修饰的字段)、实例成员变量;

局部变量,除了成员变量,其他都是局部变量,包括方法内部的变量、方法的形参、代码块中的变量(一对花括号{}称为代码块);


(2)初始值

成员变量,默认有初始值,各数据类型的初始值如下


局部变量,没有初始值,必须先初始化才能使用(初始化才会在内存中开辟空间);


2 变量的作用域和生命周期

(1)作用域

作用域,指变量的存在范围,只有在该范围内程序代码才能访问它;

当一个变量定义时,其作用域就确定了;变量根据定义的位置不同也决定了作用域不同(看变量所在的那对{});

成员变量,在整个类中都有效,可先使用再定义(但不建议);

局部变量,在定义的位置开始,到紧跟着结束的花括号为止,必须先定义再使用;


(2)生命周期

变量的作用域决定变量的生命周期,说明作用域不同,生命周期不同;

生命周期,指一个变量被创建并分配内存空间开始,到该变量被销毁并清除所占内存空间的过程;

 存在位置生命周期开始生命周期结束在内存中的位置
类变量使用static修饰的字段所在类的字节码加载到JVMJVM停止工作方法区
实例变量没有使用static修饰的字段创建所在类的对象该对象被GC回收
局部变量方法内部、方法形参或代码块初始化变量所在方法/代码块结束当前方法所在栈帧


3 局部变量的初始化和在JVM中的运行机制

局部变量定义后,必须显式初始化才能使用,因为系统不会为局部变量执行初始化操作,即定义局部变量后,系统并未为该变量分配内存空间;

直到程序为该变量赋值时,系统才为该局部变量分配内存,并将初始值保存到该内存中;

局部变量不属于任何类或实例,因为其总是保存在所在方法的栈内存中;

基本数据类型的局部变量,直接把该变量的值保存到该变量所对应的内存中;

引用数据类型的局部变量,内存存储地址,通过该地址值引用到该变量实际引用堆中对象;

栈内存中变量无需系统垃圾回收,随方法或代码块的运行结束而结束;


4 变量的定义和选择

(1)定义

<数据类型> <变量名>;
<变量名> = <初始值>;
// 或
<数据类型>  <变量名> = <初始值>;


(2)选择

a 考虑因素

考虑变量的生存时间,这会影响内存开销;

扩大变量作用域,不利于提高程序的高内聚;


b 使用成员变量和局部变量的情形

实际开发中,尽量缩小变量的作用域,因此在内存中停留时间越短,性能越高;

不要轻易使用static修饰,除了定义工具方法或变量属于类;

不要轻易使用实例变量,因为存在线程不安全的问题;

尽量使用局部变量;




二、封装思想

1 package语句

(1)概念

在开发中,存在成千上百的Java文件,若所有Java文件都在一个目录中,管理繁琐,可使用生活中的解决方案 —— 分类,但在Java中将文件夹称为包package;

专门为当前Java文件设置包名;


(2)语法格式

package <包名>.<子包名>.<子子包名>; // 表示在<包名>文件夹下的<子包名>文件夹下的<子子包名>文件夹下

必须将该语句作为Java文件中第一行代码(所有代码之前);


(3)使用

a 编译命令为 javac -d . <类名>.java

若当前Java文件中没有使用package语句,表示在当前文件夹下生成字节码文件;

若使用package语句,表示在当前文件夹下先生成包名,再在包中生成字节码文件;


b 运行命令为java <包名>.<类名>


(4)package最佳实践

a 包名定义准则

(a) 包名必须遵循标识符规范,且全部小写

(b) 企业开发中,包名采用公司域名倒写,格式为package <域名倒写>.<模块名>.<组件名>;

(如爱立信域名ericsson.com,则包名为com.ericsson.pss.util,用于进销存模块中专门存储工具类的包);


注:在Android中,若package中使用了_,则无法部署到模拟器中,可使用一个字母代替_;

(c) 自定义包名不能以java开头,因为Java的安全机制会检查


b 类的名称

类的简单名称 —— 定义类的名称

类的全限定名称 —— 包名.类名


如下,类的简单名称是PackageDemo,类的全限定名称是com.sissiy.hello.PackageDemo;

package com.sissiy.hello;

public class PackageDemo{
	public static void main(String[] args){
		System.our.println("Helo");
	}
}

不同的包可以有相同的类名;


c 在开发中,都是先有package,再在package中定义类;



(5)JDK中的包名



2 import语句

当A类和B类不在同一个包中,若A类需要使用B类,此时A类必须引入B类;

如数组的工具类Arrays类在java.util包中,Arrays类的全限定名为java.util.Arrays;

没有使用import语句,操作不在同一个包中的类需要使用全限定名;

class ImportDemo{
	public static void main(String[] args){
		int[] arr = {1,2,3,4,5,6,7};
		String ret = java.util.Arrays.toString();
		System.out.println(ret); // 打印数组  
	}
}


使用import语句,直接把某个包中的类导入到当前类中,在该Java文件中可直接使用类的简单名称;

语法格式

import <全限定名>; // 只能导入一个类

则上例更新为如下代码:

import java.util.Arrays;

class ImportDemo{
	public static void main(String[] args){
		int[] arr = {1,2,3,4,5,6,7};
		String ret = Arrays.toString();
		System.out.println(ret); // 打印数组  
	}
}


注:

编译器默认寻找java.lang包下的类,如String,因此不需导入,但不会导入java.lang的子包中的类,此时还需使用import语句;

非java.lang的包都需要导入;

若同时使用同一个包中的多个类,可使用*表示引入在当前文件中使用到的包中的所有类,语法格式如下:

import <包名>.<子包名>.*;

在Eclipse工具中,即使使用通配符*,在格式化代码时也会转换为N条import语句;


3 static import语句

如下代码,即使使用import语句,每次都要使用Arrays类名去调用静态方法;

import java.util.Arrays; 

class StaticImportDemo{
	public static void main(String[] args){
		int[] arr = {1,2,3,4,5,6,7};
		String str = Arrays.toString();
		System.out.println(str);
		Arrays.sort(arr);
		str = Arrays.toString();
		System.out.println(str);
	}
}


把Arrays类中的静态成员作为当前Java文件的静态成员,可使用静态导入功能;

语法格式

import static <类的全限定名>.<该类中的static成员名>;

import static <类的全限定名>.*; // *表示当前类的任意使用到的静态成员


上例代码更新如下

import java.util.Arrays;
// 把java.util.Arrays类中的静态方法sort导入到StaticImportDemo类中
import static java.util.Arrays.sort; 

class StaticImportDemo{
	public static void main(String[] args){
		int[] arr = {1,2,3,4,5,6,7};
		// 【注意】无法使用静态引入,因为Object类中也有toString方法,会造成指代不明错误,因此必须使用类名调用toString方法
		String str = Arrays.toString();
		System.out.println(str);
		sort(arr);
		str = Arrays.toString();
		System.out.println(str);
	}
}


注:

通过反编译工具,所谓的静态导入也是语法糖 —— 编译器的新特性;

在实际开发中不适用静态导入,因为分不清某一个静态方法/字段来源于哪个类;

Eclipse工具在格式化代码时取消所有静态导入,变成使用类名调用;


4 封装

(1)概念

把对象的状态和行为看成一个统一的整体,将二者存放在一个独立的模块中(类);

“信息隐藏” 把不需要让外界知道的信息隐藏起来,尽可能隐藏对象功能实现细节,向外暴露方法,保证外界安全访问功能;

把所有字段使用private私有化,不准外界访问;把方法使用public修饰,允许外界访问;


(2)好处

使调用者正确方便地使用系统功能,防止调用者随意修改系统属性;

提高组件的重用性;

达到组件之间的低耦合性(当某个模块发生变化时,只要对外暴露的接口不变,便不影响其他模块);


如使用封装思想设置一个人的年龄,代码实现如下

class Person{
	private String name;
	private int age;
	
	public void setAge(int age){
		if(age < 0){
			System.our.println("年龄不能为负数");
			return;
		}
		this.age = age;
	}
}

public class PersonDemo{
	public static void main(String[] args){
		Person p = new Person();
		p.setAge(18);
	}
}


5 访问权限修饰符

访问权限修饰符针对Java中封装思想,规定在一个类中能看到什么,不能看到什么;

访问权限控制有4种,访问权限修饰符有3种;

(1)private —— 私有的,表示类访问权限,即只能在本类中访问,离开本类就不能直接访问;

(2)缺省 —— 包私有,表示包访问权限,即访问者的包必须和当前定义类所在的包相同才可访问(子包也不行);

(3)protected —— 子类访问权限,表示同包可以访问,即使不同包,只要有继承关系,也可以访问;

(4)public —— 公共的,表示在当前项目中任何地方对其访问;



注:

一般,字段都使用private,为安全性而隐藏;

一般,拥有实现细节的方法使用private,不希望外界(调用者)看到该方法的实现细节;方法使用public,供外界直接调用;

// 直接暴露给外界,供调用者直接调用
public void doWork(){
	methodA();
	methodB();
	methodC();
}

// 仅仅完成部分操作,不需要调用者调用
private methodA(){}
private methodB(){}
private methodC(){}

一般,不使用缺省,即使要使用,也只是暴露给同包中的其他类;

一般,在继承关系中,父类需要把一个方法只暴露给子类,才使用protected;


6 JavaBean规范

(1)概念

JavaBean是一种Java语言编写的可重用性组件(类);


(2)必须遵循的规范

类必须使用public修饰;

必须保证有公共无参数构造器,即使手动提供带参数的构造器,也得提供无参数的构造器;

包含属性的操作手段(给属性赋值,获取属性值);


(3)分类

复杂:UI,如Button、Panel、Window类

简单:domain、dao、service组件,封装数据,操作数据库,逻辑运算等

(封装有字段,并提供getter/setter)


(4)成员

方法Method

事件event

属性Property


注:属性

attribute:表示状态,Java没有该概念,很多人把字段filed称为属性attribute,不要把成员变量称为属性;

property:表示状态,但不是字段,是属性的操作方法(getter/setter)决定,框架中使用的大多是属性;

其中,getter方法只获取某一字段存储的值;

若操作的字段是boolean类型,则不应称getter方法,而是is方法;

public <数据类型> get<首字母大写的字段名>(){
	return <字段名>;
}

setter方法只设置某一字段的值;

public void set<首字母大写的字段名>(<数据类型> <形参名>){
	<字段名> = <形参名>;
}

每个字段都要提供一对getter/setter,以后使用Eclipse工具后getter/setter自动生成;

在JavaBean中有属性的概念,只有标准情况下,字段名和属性名相同;


7 this关键字

(1)概念

this表示当前对象;


(2)this存在于两个位置

构造器:表示当前创建的对象;

方法:哪个对象调用this所在的方法,this表示哪个对象;


如下对含有this的代码进行内存分析



注:

当一个对象创建后,JVM会分配一个引用自身的引用this;


(3)this使用

a 解决成员变量和参数(局部变量)之间的二义性,必须使用this;

b 同类中实例方法间互相调用,可省略this,但不建议;



c 将当前对象作为参数传递给另一个方法;



d 将this作为方法的返回值(链式方法编程);



e 构造器重载时的互相调用,this(<实参>);必须写在构造器方法的第一行;

构造器中只能调用一个重载的构造器;


如下代码,语法没有错,但存在代码功能重复、代码维护性差的问题;

class User{
	private String name;
	privte int age;
	
	User(){

	}
	User(String name){
		this.name = name;
	}
	User(String name, int age){
		this.name = name;
		this.age = age;
	}
}


优化代码如下,this(name)表示调用参数为String类型的构造器;

class User{
	private String name;
	privte int age;
	
	User(){

	}
	User(String name){
		this();
		this.name = name;
	}
	User(String name, int age){
		this(name); // 修改处,调用User(Stirng name)
		this.age = age;
	}
}


当多个构造器重载时,或多个方法重载时,一般少参数的调用多参数的,因为参数越多,该方法考虑的未知因素越多,即功能更强大;

class User{
	private String name;
	privte int age;
	
	User(){
		this(null, 0);
	}
	User(String name){
		this(name, 0);
	}
	User(String name, int age){
		this.name = name;
		this.age = age;
	}
}


f static不能和this一起使用,因为字节码加载进JVM,static成员便存在,但此时对象还未创建,没有对象就没有this;


8 构造器和setter方法选用

(1)创建对象并给对象设置初始值的方式

a 先通过无参数构造器创建一个对象,再通过对象调用相应的setter方法;

User u1 = new User();
u1.setName("Sissiy");
u1.setAge(18);


b 直接调用带参数的构造器,创建出的对象就有初始值;

User u1 = new User("Sissiy", 18);


通过构造器和通过setter方法都可完成相同功能;


(2)给对象设置数据的方式

a setter注入(属性注入)

b 构造注入


(3)方式选择

若存在带参数的构造器,推荐构造方式;

若在构建对象时需初始化多个数据,选择构造方式,构造器需要提供多个参数,参数过多不直观,推荐setter方式;

有时,需要根据数据构建对象,推荐构造方式;


如圆对象,必须根据半径确定对象,应该在构建圆对象时确定半径值;


9 练习

判断点和圆的关系,是圆外?圆中?圆上?

class Point{
	private double x;
	private double y;
	
	Point(double x, double y){
		this.x = x;
		this.y = y;
	}
	
	public double getX(){
		return x;
	}
	public double getY(){
		return y;
	}
}

class Circle{
	private double rad;
	private double x;
	private double y;

	Circle(){}
	Circle(double rad, double x, double y){
		this.rad = rad;
		this.x = x;
		this.y = y;
	}
	/*
	这是我的思路
	public double getDis(double x1, double y1){
		return (x1-x) * (x1-x) + (y-y1) * (y-y1);
	}
	*/
	
	// 老师的思路
	public double getDis(Point p){
		return p.getX() * p.getX() + p.getY() * p.getY();
	}
	
	public int judge(double dis){
		if(dis * dis == rad){
			return 0; // 圆上
		}else if(dis > rad){
			return 1; // 圆外
		}else{
			return 2; // 圆内
		}
		
	}
}

public class PointDemo{
	public static void main(String[] args){
		Point p1 = new Point(3, 4);
		Circle c1 = new Circle(1, 0, 0);
		// double dis = c1.getDis(p1.getX, p1.getY);
		double dis = c1.getDis(p1);
		switch(c1.judge(dis)){
			case 0:
				System.out.println("圆上");
				break;
			case 1:
				System.out.println("圆外");
				break;
			case 2:
				System.out.println("圆内");
				break;
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值