第4章 面向对象(上)
4.1 类和对象
4.1.1 定义类
class and object
定义一个类:
[修饰符] class 类名
{
构造器;
成员变量;
方法;
}
修饰符:public、final、abstract
Java类名单词首字母大写,其他字母小写,单词与单词之间没有任何分隔符。
构造器
[修饰符] 构造器名(形参列表)
{
}
修饰符:public、protected、private
构造器名:与类名相同
成员变量(field)
[修饰符] 类型 成员变量名 [= 默认值]
修饰符:public、protected、private、static、final
成员变量名:第一个单词的首字母小写,后面每个单词首字母大写,其他字母全部小写
方法
[修饰符] 方法返回值类型 方法名(形参列表)
{
}
修饰符:public、protected、private、static、final、abstract
方法名:同成员变量名,建议由英文动词开头
static、类与变量
static修饰的成员不能访问没有static修饰的成员。
static修饰的成员是属于类的,类变量,类方法
没有使用static的:实例变量、实例方法
static的真正作用就是用于区分成员变量、方法、内部类、初始化块这四种成员到底是属于类本身还是属于实例。
public class Person
{
//成员变量
publicString name;
publicint age;
//方法
publicvoid say(String content)
{
System.out.println(content);
}
}
4.1.2 对象的产生和使用
创建对象的根本途径是构造器,通过new关键字来调用某各类的构造器即可创建这个类的实例。
// 第一种方法
Person p;
p = new Person();
// 第二种方法
Person p = new Person();
static修饰的方法和成员变量既可以通过类调用,也可通过方法调用;没有static修饰的方法和成员变量只能通过实例调用。
4.1.3 对象、引用和指针
类是一种引用数据类型,如同数组一样。
当一个对象被创建成功后,这个对象将保存在堆内存(heap)中,java不允许直接访问heap中的对象,只能通过该对象的引用p(存储在栈中)操作该对象。
4.1.4 对象的this引用
对象的默认引用
this总是指向调该方法的函数:谁在调用this这个方法,this就代表谁。
l 在构造器中
l 在方法中
作用:让类中的一个方法,访问该类里的另一个方法或实例。
代码略;
特别的:static修饰的方法不能使用this引用,this无法指向有效的对象。
如果调用static修饰的成员时省略了前面的主调,那么默认使用该类作为主调;
如果调用没有static修饰的成员是省略了前面的主调,那么默认使用this作为主调。
一个典型的错误
public class StaticAccessNoStatic
{
publicvoid info()
{
System.out.println("info");
}
publicstatic void main(String[] args)
{
info();
}
}
info()方法是属于实例的方法,而不是属于类的方法。
注意:Java编程是不要使用对象去调用static修饰的成员变量、方法,而是应该使用类去调用!如果确实需要在静态方法中访问另一个普通方法,则只能重新创建一个对象。
new xx.info();
4.2 方法详解
4.2.1 方法的所有属性
4.2.2 方法的参数传递机制
参数传递方法只有一种:值传递
创建一个对象时,系统内存中有两个东西:堆内存中保存对象本身,栈内存保存了引用该对象的引用变量。
4.2.3 形参个数可变的方法
在最后一个形参的类型后增加三点(…),则表明该形参可以接受多个参数值,多个参数值被当成数组传入。
形参个数可变的参数本质就是一个数组参数。
对比:
public static void test(int a, String...books);
test(5, "java", "c++","CPP");
public static void test(int a, String[]books);
test(5, new String[] {"java","c++", "CPP"});
4.2.4 递归方法
4.2.5 方法重载
方法重载的要求就是两同一不同:同一类中方法名相同,参数列表不同。至于方法返回值类型、修饰符等,与方法重载没有任何关系。
不推荐重载的方法里包含长度可变的形参。
4.3 成员变量和局部变量
4.3.1 成员变量和局部变量
成员变量是指在类里定义的变量,也就是field;
又分为类变量和实例变量;
局部变量是指在方法里定义的变量;
分为形参、方法局部变量和代码块局部变量;
4.3.2 成员变量的初始化和内存中的运行机制
4.3.3 局部变量的初始化和内存中的运行机制
4.3.4 变量的使用规则
类成员变量 < 方法局部变量 < 代码块局部变量
尽可能的缩小作用范围
4.4 隐藏和封装
4.4.1 理解封装
该隐藏的隐藏,该暴露的暴露。
4.4.2 使用访问控制符
private:当前类访问权限
default:包访问权限
protected:子类访问权限
public:公共访问权限
如果一个java源文件里定义的所有类都没有使用public修饰,则这个java源文件的文件名可以是一切合法的文件名;但如果一个java源文件里定义了一个public修饰的类,则这个源文件的文件名必须与public修饰的文件名相同。
访问控制符的使用原则:
l 类的大部分成员变量都应该使用private修饰,只有一些static修饰的。类似全局变量的成员变量才可能考虑public修饰。工具方法应该使用private修饰。
l 如果某个类主要是作为其他类的父类,该类包含的大部分方法可能仅希望被其子类重写,而不是调用,则应该使用protected修饰这些方法。
l 希望暴露出来给其他类调用的方法应该使用public修饰。特别是类的构造器,还有外部类。
4.4.3 package、import和import static
java引入包机制,提供类的多层命名空间,用于解决命名冲突、类文件管理等问题。java允许将一组功能相关的类放在同一个package下,从而组成逻辑上的类库单元。
package packageName;
一旦在java源文件中使用了package语句,意味着该源文件里定义的所有类都属于这个包,位于包中的每个类的完整类名都应该是包名和类名的组合(lee.Hello.java)。
java的包机制需要两个方面的保证:
1. 源文件里使用package语句指定包名;
2. class文件必须放在对应的路径下。
包名要求全是小写字母。
如果没有显示指定package语句,则处于默认包下,同一个包下的类可以自由访问,无需前缀。
父包和子包存在逻辑上的关系(模块与整体),但在用法上不存在任何关系,如果父包中需要使用子包中的类,则必须使用子包的全名,而不能省略父包部分。
使用不同包中的其他类要加全名???
java引入了import,import可以向某个java文件中导入指定包层次下某个类或全部类。
import package.subpackage…ClassName;
ex:
importlee.sub.Apple;
importpackage.subpackage…*;
java默认导入了java.lang下的所有类。
静态导入:用于导入指定类的某个静态成员变量、方法或全部静态成员变量、方法,使用import static语句。(类变量、类方法)
ex:
import static package.subpackage…ClassName.fieldName| methodName;
import static package.subpackage…ClassName.*;
import可以省略写包名,而import static则可以连类名都省略。
4.4.4 java的常用包
java.lang
java.util
java.net
java.io
java.text:格式化
java.sql
java.awt:Abstract Windows Toolkits
java.swing:GUI
4.5 深入构造器
4.5.1使用构造器执行初始化
4.5.2 构造器重载
多个构造器的形参列表不同,称之为构造器重载。
构造器必须使用new关键字来调用。
构造器B必须完全包含构造器A,如果存在这种关系,则可以在方法B中使用this关键字调用方法A。
一小段代ex:
public Apple(String name, String color,double weight)
{
this(name,color);
this.weight= weight;
}
使用this调用另一个重载的构造器只能在构造器中使用,而且必须作为构造器执行的第一条语句。
4.6 类的继承
4.6.1 继承的特点
java的继承通过extends关键字实现。子类不能获得父类的构造器。
语法格式如下
修饰符 classSubClass extends SuperClass
{
// 定义类部分
}<span style="font-family: 'Microsoft YaHei'; background-color: rgb(255, 255, 255);"> </span>
java没有多重继承,只有单继承。
4.6.2 重写父类的方法
子类重写父类的方法。鸟能飞,鸵鸟不能飞,只能跑。
方法重写/方法覆盖:子类覆盖父类的方法。
原则:两同两小一大
两同:方法名相同,形参列表相同;
两小:子类方法返回值类型小于等于父类的,子类方法抛出的异常类应小于等于父类的;
一大:子类方法的访问权限应比父类的更大或相等。
覆盖的方法和被覆盖的方法,要么都是实例方法,要么都是类方法(static)。
子类对象无法访问父类中被覆盖的方法,但可以通过super(被覆盖的是实例方法)或父类类名(被覆盖的是类方法)在子类对象中调用该方法。
父类方法具有private访问权限,则该方法对子类隐藏,子类可以另行定义同名方法。
比较
overload:重载,同一类的多个同名方法名之间
override:覆盖,重写,子类和和父类同名方法之间
4.6.3 super限定
通过super调用被覆盖的是实例方法,添加一个方法即可。
public void callOverridedMethod()
{
super.xx();
}
super用于限定对象调用从父类继承得到的实例变量或方法。super不能出现在static修饰的方法中,这一点和this一样。
子类定义与父类同名的实例变量,子类会隐藏父类实例变量()还为它分配内存,而不是重新赋值。
4.6.4 调用父类的构造器
类似于一个构造器调用另一个重载的构造器,但是是使用super完成的。
父类构造器先于子类构造器执行,java.lang.Object构造器最先执行。
public Sub(double size, String name, Stringcolor)
{
super(size,name);
this.color= color;
}
4.7 多态
4.7.1 多态性
如果编译时类型和运行时类型不一致,就可能出现多态(Polymorphism)。
BaseClass ploymophicBc = new SubClass();
//有限地执行子类,父类到子类,将复杂的东西简单处理。
向上转型(upcasting):将子类对象直接赋值给一个父类引用变量
编译时类型是BaseClass而运行时类型是SubClass。
方法具有多态性,而对象的实例变量并没有具有多态性,对于book,程序中输出的并不是SubClass的实例变量,而是BaseClass的实例变量。
引用变量编译时只能调用其编译时具有的方法,但运行时则执行它运行时类型所具有的方法。通过引用变量来访问器包含的实例变量时,系统总是试图访问它编译时类型所定义的成员变量,而不是运行时类型所定义的成员变量。
4.7.2 引用变量的强制类型转换()
子类到父类不担心。
引用变量的类型转换与4.7.2正好相反,是由父类到子类,由简单到复杂。
l 基本类型转换只发生在数值类型之间,boolean只占一位,big太低。
l 引用类型之间的转换发生在在家族内,使用不慎会有ClassCastException异常。
使用instanceof运算符判断是否可以成功转换。
if (objPri instanceof String)
{
Stringstr = (String)objPri;
}
4.7.3 instanceof运算符
instanceof运算符的前一个操作数通常是一个引用类型变量,后一个操作数通常是一个类(或者接口),它用于判断前面的对象是否是后面的类、子类、实现类的实例,前后一般应该具有父子关系。
System.out.println("字符串是否是Comparable接口的实例:"+ (hello instanceofComparable));
4.8 初始化块
4.8.1 使用初始化块
初始化块是java中可以除成员变量,构造器和方法的第四种成员(没用的),隐式执行,全部执行。
当java创建一个对象时,系统先为该对象的所有实例变量分配内存(该类已经被加载过了),接着程序开始对这些实例变量执行初始化:先执行初始化块,声明时指定的初始值,再执行构造器。