5.this
this变量的使用:
1.用在成员变量前面 (当前对象的sex属性)
2.成员方法前 (当前的对象)
3.代码冗余:使用this可以调用自身的另外一个构造函数
4.this作为返回值
public class Person { private int id; private String name; private boolean sex;
public Person() {
}
public Person(boolean s) { sex = s; }
public Person(boolean s,String n) { sex = s; name = n; }
main() { Person p1 = new Person(); } } |
public class Person { private int id; private String name; private boolean sex; private itn tel;
public Person() {
}
public Person(int id,String name, boolean sex) { this.id = id; this.name = name; this.sex = sex;
//Person(sex,name); this(sex,name) 构造函数内部,如果要访问另外一个构造函数,使用this关键字
this.eat(); 就近原则 JVM认为这两个sex都是形参 这时 就有必要区分作为形参的sex 和 当前对象的sex属性 创建对象,对象内部除了包含有所属类中定义的成员变量外,还包含一个变量 命名为this ,this 的值是当前对象的地址 换句话说,this就是指向该对象本身
this变量的使用: 1.用在成员变量前面 (当前对象的sex属性) 2.成员方法前 (当前的对象) 3.代码冗余:使用this可以调用自身的另外一个构造函数 4.this作为返回值
}
public Person(boolean sex,String name) { this.sex = sex; this. = name; }
public void eat() {
}
main() { Person p1 = new Person(); } }
public class Leaf { int i = 0; Leaf(int i ) { this.i = i; }
//方法的返回值类型:返回值的数据类型 //现在返回的是某个对象的“地址” 那么返回值类型? //通过返回值得到的地址,就能找到对应的“对象” //所以返回值的类型,就应该是地址标记的对象所属的类型 Lesf increment() { i++; //i属于调用该方法的对象leaf return this; //返回的是this的值,也就是当前对象的地址 }
void print() { System.out.println("i = " + i); }
public static void main(String[] args) { Lesf leaf = new Leaf(100); Leaf l1 = leaf.increment(); Leaf l2 = leaf.increment(); l2.print();
leaf.increment().increment().print(); } } |
6.static:
1.成员变量的前面
2.成员方法的前面
3.{} 前面
public class Person { private static int id; 静态成员变量在类加载(运行之前)的时候就开始分配空间(dateSegment)并默认初始化
静态成员变量和具体某个对象没有关系~!( 因为静态变量是在加载时初始化的,而对象创建在运行期) 也可以说:静态变量是“类层面的变量” 而非静态变量是“对象层面的变量”
既然静态变量属于类,那么可以通过类直接访问静态变量。(因为静态变量和对象无关) 推荐使用:“类名.静态成员变量”
private int age; //非静态成员变量什么时候初始化?-----运行创建对象的时候
public void m1() {}
public static void m2() { 静态方法:都是属于类层面的 和具体对象无关,通过"类名.静态方法()"访问
}
static { 静态代码块 通常用于资源初始化
}
public static void main(String[] args) { main方法为什么要定义成static 类型的 现有的技术储备:加载 - 运行(main) - 创建对象. 在main方法执行前,内存中不存在任何对象,只有类 只能通过类调用main方法,所以main方法要定义成static类型的 } }
|
public class Person { {System.out.println("普通代码块"); }
static { System.out.println("静态代码块"); }
public Person() { System.out.println("构造函数"); }
public static void main() { Person p = new Person(); } } |
-----执行顺序
静态代码块 -> 普通代码块 -> 构造函数
public static void main(String[] args) {
静态方法中直接访问非静态方法,一定出错
因为:静态(变量、方法、代码块)是属于“类层面”的,和具体对象无关
或者说,无论内存中存不存在对象,静态的一定存在
}
public class Cat { private static int sid = 0; private String name; int id;
Cat(String name) { this.name = name; id = sid++; }
public void info() { System.out.println ("My name is " + name + ",No." + id); }
public static void main(String[] args) { Cat.sid = 100; //类名访问静态变量 Cat mimi = new Cat("mimi"); mimi.sid = 2000; //对象访问静态变量 Cat pipi = new Cat("pipi"); mimi.info(); pipi.info(); } } |
7.继承
代码冗余 可能发生在(方法内部、类的内部),冗余代码往外踢,遵循“对等原则”
通过继承,子类自动拥有了父类的成员(成员变量和成员方法)
java只支持单继承,不允许多继承(extends)。接口可以(interface)
public class Person { private int id; private String name; private boolean sex;
public void eat() { System.out.println("Person is eating"); } }
public class Teacher extends Person{
private int gl; //教龄 private String zc; //职称
public void teach() { System.out.println("Teacher is teaching"); }
}
public class Student extends Person {
private String school; public void study() { System.out.println("Student is studying"); } } |
解决了代码冗余问题,简洁明了,但是高耦合,Teacher类和 Student类太过依赖 Person类
8.package
操作一:
1.在磁盘"e:\a"作为开发目录,存储源文件
2."e:\a"新建两个java源文件,Test.java和 Person.java
public class Person { public static int id = 100; }
public class Test { public static void main(String[] args) { Person p = new Person(); } } |
编译Test.java就同时得到两个字节码文件,Test.class和 Person.class
操作二:
1.Test类中可能会用到很多其他的类,为了方便管理 ,我们往往将这些类分类管理
约定:没有配置classpath,当前的目录结构是"e:\a"
2.Person.class存储在"e:\a\p" //相对路径 p
在Person.java源文件的首行使用关键字package
3.Teacher.class存储在"e:\a\t" //相对路径 t
4.Student.class存储在"e:\a\s" //相对路径 s
路径:
相对路径:相对当前上下文
绝对路径:磁盘文件结构:盘符,如:"e:\a"
package t; public class Teacher { public static int id = 200; }
package s;
public class Student { public static int id = 300; } |
5.这样做编译后,出错:
"错误:无法访问Person类"
6.正确的编译顺序:
先编译被依赖的(Test 依赖于 Person,先编译 Person)
JDK5.0之后,java源文件有可能会影响到.class的运行,先把Person.java源文件移除掉
接着把编译后得到的Person.class字节码文件,转移到p文件夹内
接着编译Test.java,依旧报错
7.
"错误:找不到符号"
JVM 首先在当前上下文中寻找Person.class提示找不到符号
解决方法: 告诉JVM 到p文件夹下加载Person.class
语法:在Person前加上 p.
class Test { public static void main(String[] args) { p.Person pp = new p.Person(); } } |
8. 再次编译Test.java不出错,运行成功
操作三:
想把Student放的更深一点,s下的s1,s1下的s2,s2下的s3内
package s.s1.s2.s3; //磁盘目录/文件夹=====包
public class Test { public static void main(String[] args) { s.s1.s2.s3.Student s = new s.s1.s2.s3.Student(); //全路径类名 -> 反射 } } |
每次都要书写全路径类名,过于繁琐,于是使用 import 关键字
imports.s1.s2.s3.Student;
imports.s1.s2.s3.Student是相对路径,相对于当前上下文以及classpath
所以可以把包的父路径配置到classpath之中去
public class Test { public static void main(String[] args) { System.out.println(" "); } } |
System:系统内置类,D:jdk1.7.0_60\jre\lib\rt.jar;
rt.jar是压缩文件的一种,压缩多个类 (java特有)
.rar .zip压缩格式
rt-> java-> lang ->System.class
java中lang包的类,JVM 会自动加载,不需要import的导入
"D:jdk1.7.0_60\jre\lib\rt.jar"在安装JDK环境的时候,自动配置都classpath里了。
import 包名.类名; import 包名.*;
9.权限控制
子类继承父类,那么子类就会“自动拥有”父类的成员。
现在考虑的是:子类能不能“直接访问”从父类继承的成员?
成员变量定义前可以使用四种修饰关键字:
private int id;
default int id;
protected int id;
public int id;
成员变量位置:类内部 同一个包不同类内(含继承) 不同包不同类(含继承) 不同包不同类(没有继承) private Y N N N default Y Y N N protected Y Y Y N public Y Y Y Y |
private : 类层面的
default : 包层面的
protected : 子类层面
public : 所有
类内部定义了"private"修饰的成员,那么现在测试的是同一个类的内部是不是可以直接访问该变量?
10.重写
子类继承父类的方法,如果对继承的方法的实现“不满意”,
那么在子类中可以重新定义继承方法的具体实现:重写
重载:一个类中的若干方法,如果满足:方法名相同,形参不同
重写:子类“重写”父类继承的方法
注意 public void eat () {}
修饰符 返回值 方法名 形参列表 方法体
-------------------------------------------------------
父类中的方法 相 相 相 不
子类重写的方法 同 同 同 同
重写方法必须和被重写方法具有相同方法名称,参数列表和返回类型
权限修饰符:从严格到宽松的顺序:private default protected public
子类的权限修饰,不能比父类的严格
-----父类----- public class Person { public void eat() { System.out.println("Person is eating"); } } |
-----子类------ public class Teacher extends Person { public void eat() { System.out.println("Teacher is eating"); } 09
public void teach() { System.out.println("Teacher is teaching"); } } -----子类------ public class Student extends Person { public void eat() { System.out.println("Student is eating"); }
public void study() { System.out.println("Student is studying"); } } |
11.super
创建一个对象,堆内存分配空间,包含所属类中定义的成员变量+this(本身的地址)+super(父对象的地址)
子类的构造函数必须调用父类的构造函数
如果调用super,必须写在子类构造方法的第一行
使用this(argument_list)调用本类的另外构造方法
如果子类的构造方法中没有显示的调用父类的构造方法,则系统默认调用父类无参的构造函数
如果子类的构造方法中既没有显示调用父类构造方法,而父类中又没有无参的构造函数,则出错
同一个构造函数中,如果同时出现this和super,this优先
class FatherClass { public int value;
public void f() { value = 100; System.out.println("FatherClass.value=" + value); } }
class ChildClass extends FatherClass { public int value;
public ChildClass() { super(); }
public void f() { super.f(); value = 200; System.out.println("ChildClass.value" + value); System.out.println(value); System.out.println(super.value); } }
public class Test { public static void main(String[] args) { ChildClass cc = new ChildClass(); cc.f(); } } |
super:
super.成员变量
super.成员方法
super() 调用父类的无参构造方法
return super
class Person { public in id; }
class Teacher extends Person { public int age; } public Teacher() { //子类构造函数的第一行“一定”是调用父类的构造函数(先创建父对象) //通过关键字super来调用父类构造函数 //系统默认调用父类无参构造函数super()
super(); } }
class Test { main() { Teacher t = new Teacher(); } }
---------------------------------------
public class Person { protected int id;
如果没有t显示声明构造函数,那么系统自动提供“无参构造函数” 如果显示定义了构造函数,那么系统将不再提供“无参构造函数
class Person(int id) { this.id = id; } } |
class SuperClass { private int n;
/* SuperClass() { System.out.println("SuperClass()"); } */
SuperClass(int n) { System.out.println("SuperClass("+n+")"); //300 this.n = n; } }
class SubClass extends SuperClass { private int n;
SubClass(int n) { //super(); System.out.println("Subclass(" + n + ")"); this.n = n; }
SubClass() { super(300); System.out.println("SubClass()"); } }
public class TestSuperSub { public static void main(String arg[]) { //SubClass sc1 = new SubClass(); Subclass sc2 = new Subclass(400); } }
|
class A { protected void print(String s) { Systme.out.println(s); } A() { print("A()"); public void f() { print("A:f()"); } }
class B extends A { B() { print("B()"); // 把B()存储内存单元的地址作为实参,传给形参变量 } public void f() { // 重写 }
public static void main(String[] args) { B b = new B(); } } }
输出结果: A() //; B() //; |
class Person { private String name; private String location;
public Person(String name) { this.name = name; this.location = "beijing"; }
public Person(String name,String location) { this.name = name; this.location = location; }
public String info() { return "name:" + name + "location:" + location; } }
class Student extends Person { private String school;
public Student(String name,String school) { !!!super(); 同一个构造函数同属出现this和super,this优先 this(name,"beijing",school); }
public Student(String name,String location,String school) { super(name,location); //调用super,创建父类对象 this.school = school; }
public String info() { return super.info() + "school" + school; } }
public class Test { public static void main() { Person p1 = new Person("A"); Perosn p2 = new Person("B","shanghai"); Student s1 = new Student("C","S1"); System.out.println(p1.info()); } } |
12.Object
定义类:如果一个类没有显示的声明他的父类,那么约定继承自“Object”
所以也可以说:Object 是所有类的公共父类
JDK 的安装目录下,有一个src.zip(默认源文件包)
Object 类中定义的11的成员方法,这11个方法有2个是 protected 修饰,其余都是
public 修饰,所以所有类都能继承这11个方法,并且能直接使用
1.反射 1个 .getclass
2.垃圾回收 1个 finalize
3.克隆 1个 clone
4.相等 1个 equals + hashCode
5.线程 5个 wait notify
6.字符串 1个 toString
12.1 .toString()
+ 如果"+"两侧的操作数有一个是字符串,就表示"连接"操作(即把另一个
操作数也变成字符串,然后执行字符串连接操作)
class Person {}
public class Test{ public static void main(String[] args) { //先把int 型的100转换成“100” //原因:int 基本类型对应一个引用类型Integer //Integer 继承了Object 提供的toString()方法,并且重写了toString方法 //把int 整数转化成字符串类型 System.out.println(100+"200");300
Person p = new Person(); System.out.println(p); //对象作为println()方法的参数,观察输出结果:Person@7d206f0 //其实是p.toString()的返回值,作为println()方法的参数输出 //也就是:输出的是对象的toString()的返回值 //即“Person@7d206f0”是Object的toString()方法的默认返回值 //Object 类的 toString() //return getClass().getName()+"@"+Integer.toHexString(hashCode());
// y = f(x) //x = f-1(y) //哈希函数,单向不可逆 //已知x通过hash函数可以得到与之对应的唯一的y } } |
12.2. equals()
class Mao { String name; String color;
Mao(String name,String color) { this.name = name; this.color = color; } }
public class Test { public static void main(String[] args) { Integer i1 = new Integer(1); Integer i2 = new Integer(1); Integer i3 = i2;
/* public Integer(int value) { this.value = value; }
public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intvalue(); } return false; }
public int intvalue { return value; } */
System.out.println(i1==i2); //false System.out.println(i3==i2); //true
System.out.println(i1.equals(i2)); //根据分析,i1和i2是分别指向两个对象 //而equals默认比较就是比较i1和i2的值,所以比较结果是false //但是运行结果是true //只能是Integer类重写了Object类的equals()
//构建Integer对象时,两个实参(基本类型int)的“==”比较
} } |
生活中有这样一种描述:“相等” “相同” “一样的”
java语法:
1.== 值的比较
基本数据类型:int i= 10; int j = 20; boolean flag=i==j;
//flag= false;
2.equals
当进行两个对象是否“相同”的比较判断时,使用Object类提供的equals方法
1.equals()方法应用的情形:
用于两个对象是否“相同”的情景
2.语法:
obj1.equals(obj2);
3.方法的返回值:
如果obj1和obj2“相同”返回true,不同返回false;
4.何为“相同”何为“不同”
结合具体业务情况
5.如果不指定“相同”的条件,JVM怎么判断?
Object父类中的equals方法已经对两个对象“相同”做了默认设置
publicboolean equals(Object obj) {
return(this == obj);
}
所以:默认比较的还是两个对象的地址
6.当需求涉及到自定义类的对象进行相同比较时,一般都需要重写equals方法
13.对象转化
需求场景:继承 子类 父类
1. 子类类型的变量去指向子类
Teacher t =new Teacher();
2.父类类型的变量指向子类(推荐使用)
Person p1 =new Teacher();
Person p2 =new Student();
3.问题:父变量指向子类对象有什么特殊的地方 ?
3.1 父变量可以访问子类从父类继承而来的方法和属性
3.2 父变量不可以访问子类新增的成员
4. 向上转型 (父变量指向子类对象)
5. instanceof (操作符)
左侧:变量
右侧:类名
引用型变量instanceof 类名
含义:引用型变量是不是类或者其子类的对象(实例)?
Person p1 =new Person();
Person p2 = newTeacher();
System.out.println(p1instanceof Preson); //变量p1指向的对象是不是Person类型的对象? true
System.out.println(p1instanceof Teacher); //
class Person { String name;
public void eat() { System.out.println("Person is eating"); } }
class Tescher extends Person { String zc;
public void teach() { System.out.println("Teacher is teaching"); } }
class Student extends Person { String xh;
public void study() { System.out.println("Student is studying"); } }
public class Test { public static void main(String[] args) { Teacher t = new Teacher(); Student s = new Student();
Person p1 = new Teacher(); Person p2 = new Student(); } } |
//重写Cat 的 equals()
class Cat { private String name; private String color;
public Cat(String name,String color) { this.name = name; this.color = color; } }
public boolean equals(Object obj) { //首先确定obj的类型 if (!obj instanceof Cat) { return false; }
//比较c1和c2的name属性,color属性是否相同 //问题:this能不能访问到name属性----能,name属性属于调用equals()方法的对象,即c1 //问题:obj能不能访问到name属性----不能,父变量obj指向子类对象c2,而name属性是子类对象c2新增的
//问题:父变量怎么去访问子类新增的成员? //答:(向下转型)强制类型转换,把父变量转换为子类变量(大的给小的)
Cat c = (Cat) obj; //this.name.equals(obj.name);
return this.name.equals(c.name);
}
main() { Cat c1 = new Cat("A","A"); Cat c2 = new Cat("A","A");
System.out.println(c1.equals(c2)); } |
14. 多态(访问并继承的方法)
//父类: class Person { public void eat() { System.out.println("Person is eating..."); } }
//子类1 class Teacher extends Perosn { public void eat() { System.out.println("Teacher is eating..."); } }-
//子类2 class Student extends Person { public void eat() { System.out.println("Student is eating..."); } }
//测试类 父变量指向子类对象,然后通过父变量访问子类从父类继承并重写的方法 //问题:方法在父类中有,每个子类对中也有,那么通过父类访问这个方法,这个方法到底是谁的?
public class Test { private static void main(String[] args) { Person p1 = new Teacher(); p1.eat(); //Teacher is eating...
Person p2 = new Student(); p2.eat(); //Student is eating... } } |
前提条件:
1. 存在继承关系
2. 子类重写了从父类继承的方法
3. 父变量指向子类对象
结论:
多态(动态绑定)通过父类调用方法:那么这个方法是:子类重写后的方法
那么父变量具体指向哪个子类对象,这个方法就是该子类的对象
动态:父变量理论上可以指向任何一个子类对象,比如:p=new Student();p=new Teacher();
绑定:访问的方法,和父变量当前指向的那个子类对象“绑定”
15.抽象方法/类
多态的效果:父变量访问方法,方法是父变量当前指向的那个子类对象重写后的方法
问题:父类中的方法的方法体无论如何都执行不了
自然想到:父类方法的方法体既然不能被访问到,那么它就没有实现的必要
====> 结论:在多态环境下,父类被子类重写的方法,只有“定义”的必要,没有“实现”的必要
只要{}存在,方法就是实现了。即使{}里面什么都没有,这叫空实现
方法定义:权限 返回值类型 方法名()
public void eat()
但问题又来了:这违背了我们之前掌握的理论:方法遵循“定义”“赋值/实现”“访问”
====> 解决方法:在方法的定义中假如关键字“abstract”(抽象的)
结论:
抽象方法,只有定义,没有实现。称为“抽象方法”
抽象类:包含抽象方法,一定要“定义”成抽象类 ===>即使没有包含抽象方法,这个类一样可以定义成抽象类
特性:
抽象方法必须必重写(完善实现)
抽象类“天生”就是被继承的
抽象类不能被直接实现=====>不能被实例化
16.final
默认名称全大写
1.修饰变量 不变的变量,即变量值不能更改
2.修饰方法(不能被重写)
3.修饰类(不能被继承);
class Person { private String name; // 变量,属性 attribute property
public Person(String name) { this.name = name; }
// 取值方法 getter或者setter方法命名约定:set/get + attribute首字母大写 public String getName() { // get方法后缀首字母变小写后得到的单词:property return name; }
// 赋值 public void setName(String name) { this.name = name; } } class Test { public static void main(String[] args) { Person p = new Person("zs");
// 获取对象的private属性值 System.out.println(p.name); // error 封装 System.out.println(p.getName()); // zs
// 重新给name属性赋值 p.setName("ls"); } }
重写@Override @Override public void eat() { // TODO Auto-generated method stub super.eat(); } |