封装和继承
1. super关键字
super关键字来访问父类的成员
super只能出现在子类的方法和构造方法中
super调用构造方法时,只能是第一句
super不能访问父类的private成员
2.继承条件下的构造方法
继承条件下构造方法的调用规则
子类构造方法没有通过super显式调用父类的有参构造方法,也没通过this显式调用自身其他构造方法
系统默认调用父类的无参构造方法
子类构造方法通过super显式调用父类的有参构造方法
执行父类相应构造方法,而不执行父类无参构造方法
子类构造方法通过this显式调用自身的其他构造方法,在相应构造方法中应用以上两条规则
3. 子类可以继承父类哪些内容
子类继承父类的什么?
继承public和protected修饰的属性和方法,不管子类和父类是否在同一个包里
继承默认权限修饰符修饰的属性和方法,但子类和父类必须在同一个包里
private修饰的属性和方法不能被继承
4. 子类不能继承父类哪些内容
5. 什么时候用继承?
何时使用继承?
继承与真实世界类似
只要说“猫是哺乳动物”,猫的很多属性、行为就不言自明了
藏獒是一种狗
继承是代码重用的一种方式
子类与父类符合is-a的关系 使用继承描述
将子类共有的属性和行为写在父类中
子类独有的属性和行为写在子类中
6. static关键字
static关键字实现原理:
1.对象的创建过程
1.1 先将当前对象对应类信息文件加载到方法区,随后初始化静态相关的信息
加载之前先检查类信息文件是否已经被加载,如果没有被加载则加载,如果加载过,不再重复加载
类信息文件只加载一次/类只加载一次
1.2 在堆中开辟空间 此时实例属性有默认值
1.3 将堆中的地址赋值给栈中的引用 完成对象的创建
2.名词解释 方法区 永久代 元数据区
方法区属于SUN公司制定的一个规范 举例:JVM是一个规范 hotspot是JAVA默认具体虚拟机名称
JDK1.7之前称之为永久代
JDK1.8改名为元数据/元空间
6.1 修饰属性
被static修饰的属性 称之为静态变量 内存中只存在一份 不属于任何对象 属于整个类 可以被当前类所有对象共享
静态变量不推荐使用通过对象(对象名加点)访问 推荐使用访问方式:
1.本类中直接访问
2.其他类通过类名加点访问
静态变量和实例变量区别、?
类变量(静态变量)
被static修饰的变量
在内存中只有一个拷贝
类内部,可在任何方法内直接访问静态变量
其他类中,可以直接通过类名访问实例变量
没有被static修饰的变量
每创建一个实例,就会为实例变量分配一次内存,实例变量在内存中有多个拷贝,互不影响
package com.atguigu.test1;
/**
* static 修饰属性/成员变量/实例变量/字段
* 被static修饰的属性 称之为静态变量 内存中只存在一份
* 不属于任何对象 属于整个类 可以被当前类所有对象共享
*
* 属性:当某个字段拥有了get以及set方法 才可以被称之为属性
* 字段:直接写在类中的变量 就是字段
* */
public class Student {
String name;
int age;
static String gradeName = "三年二班";
public static void main(String[] args) {
Student stu1 = new Student();
stu1.name = "赵四";
stu1.age = 20;
stu1.gradeName = "3年3班"; // stu1中修改静态属性,其他对象也会同步
System.out.println(stu1.gradeName); // 3年3班
Student stu2 = new Student();
stu2.name = "广坤";
stu2.age = 21;
System.out.println(stu2.gradeName); // 3年3班
Student stu3 = new Student();
stu3.name = "大拿";
stu3.age = 22;
System.out.println(stu3.gradeName); // 3年3班
// 1.以上三个对象相互之间有没有关系?
// 有关系:这三个对象都源自于同一个类所创建
// 没有关系:这三个对象在内存中 有三块独立的空间 相互之间不影响
// 因为这三个对象在内存中属于独立的内存空间 属性都属于实例级别的
// 每创建一个对象 就会在内存中存在一份 所以如果多个对象有共同的属性值 那么每次创建都会重复赋值一份 比较浪费内存空间
// 推荐做法:将多个对象共同使用的属性值 设置为静态级别的 内存中只存在一份
// 不属于任何对象 属于整个类 可以被当前类所有对象共享
}
}
package com.atguigu.test1;
/**
* 使用static关键字模拟 饮水机饮水过程
* */
public class DrinkWater {
static int capacity = 100; // 单位 L
String name;
public void getWater(){
if (capacity > 0){
capacity -= 2;
System.out.println(name + "接水2L,还剩余" + capacity + "L");
}else{
System.out.println("没水了");
}
}
public static void main(String[] args) {
DrinkWater zs = new DrinkWater();
zs.name = "赵四";
zs.getWater(); // 赵四接水2L,还剩余98L
DrinkWater dn = new DrinkWater();
dn.name = "大拿";
dn.getWater(); // 大拿接水2L,还剩余96L
DrinkWater gk = new DrinkWater();
gk.name = "广坤";
gk.getWater(); // 广坤接水2L,还剩余94L
}
}
6.2 修饰方法
static修饰方法:本类中直接访问 其他类 通过类名访问
package com.atguigu.test3;
/**
* static修饰方法:本类中直接访问 其他类 通过类名访问
* */
public class Test1 {
public static void m1(){
System.out.println("static修饰的m1方法");
}
public static void main(String[] args) {
m1();
}
}
class A{
public static void main(String[] args) {
Test1.m1(); // static修饰的m1方法
}
}
6.3 修饰代码块
static修饰代码块 :静态代码块 随着类的加载而执行 多个静态代码块 按照书写顺序执行
每个只执行一次 因为类只加载一次
什么时候加载类?
1.new对象
2.访问当前类中的静态信息
什么使用时候静态代码块?
静态代码块中通常用于数据初始化操作 或者 执行一切前置的必要操作
总结:只需要执行一次 而且必须提前完成
package com.atguigu.test3;
/**
* static修饰代码块:静态代码块 随着类的加载而执行 多个静态代码块 按照书写顺序执行
* 每个只执行一次 因为类只加载一次
*
* 什么时候加载类?
* 1.new对象
* 2.访问当前类中的静态信息
*
* 什么时候使用静态代码块?
* 静态代码块中通常用于数据初始化操作 或者 执行一些前置的必要操作
* 总结:只需要执行一次 而且必须提前完成
* */
public class Test2 {
static int num; // 默认值为0
int age;
static {
System.out.println("静态代码块1");
}
static {
System.out.println("静态代码块2");
}
static {
System.out.println("静态代码块3");
}
public static void main(String[] args) {
// 不管创建多个对象,静态代码块只会执行一次
// Test2 t1 = new Test2();
// Test2 t2 = new Test2();
// Test2 t3 = new Test2();
// 不管访问多少次静态信息,静态代码块只会执行一次
System.out.println(num); // 0
System.out.println(num); // 0
System.out.println(num); // 0
System.out.println(num); // 0
}
}
普通代码块/实例代码块
随着对象的创建而执行 每创建一次对象就执行执行
package com.atguigu.test3;
/**
* 普通代码块/实例代码块
* 随着对象的创建而执行 每创建一次对象就执行一次
* */
public class Test3 {
static int num;
{
System.out.println("普通代码块1");
}
{
System.out.println("普通代码块2");
}
public static void main(String[] args) {
// 访问类属性不会执行普通代码块
System.out.println(num); // 0
// 创建对象就会执行普通代码块的内容
Test3 t1 = new Test3();
System.out.println("-------------------");
Test3 t2 = new Test3();
System.out.println("-------------------");
Test3 t3 = new Test3();
}
}
7. 访问规则
静态与实例访问规则:
同级别互相直接访问 (实例访问实例 直接访问 静态访问静态 直接访问)
实例访问静态 直接访问 (因为可以访问实例的时候 肯定创建完对象 也就是说类肯定加载完了)
静态访问实例 先new对象 (再通过对象名加点访问)
在实例方法里不可以定义static变量
package com.atguigu.test4;
/**
* 静态与实例访问规则:
* 同级别互相直接访问 (实例访问实例 直接访问 静态访问静态 直接访问)
* 实例访问静态 直接访问 (因为可以访问实例的时候 肯定是创建完对象了 也就是说类肯定加载完了)
* 静态访问实例 先new对象(再通过对象名加点访问)
* */
public class Test1 {
String field1;
static String field2;
// 实例方法之间可以直接访问
public void m1(){
m3();
}
public void m2(){
m1();
}
public void m3(){
m2();
}
// 静态方法之间可以直接访问
public static void m4(){
m6();
}
public static void m5(){
m4();
}
public static void m6(){
m5();
}
public void test1(){
// 实例方法中可以直接访问静态方法
test2();
}
public static void test2(){
// 静态方法中访问实例方法 需要先创建实例对象
Test1 t1 = new Test1();
t1.test1();
}
public static void main(String[] args) {
}
}
8. static练习题
模拟实现选民投票过程:一群选民进行投票,每个选民只允许投一次票,
并且当投票总数达到100时,就停止投票
package com.atguigu.test5;
public class Test1 {
String name;
static int count;
public Test1(String name){
this.name = name;
}
public boolean addCount(){
if (count < 100){
count++;
System.out.println(name + "投1票,当前投票总数为:" + count + "票");
return true;
}else{
System.out.println("投票总数达到100,无需再投");
return false;
}
}
public static void main(String[] args) {
Test1 t1 = new Test1("张三");
t1.addCount();
Test1 t2 = new Test1("李四");
t2.addCount();
for (int i = 1;i < 110;i++){
Test1 t = new Test1(i + "号民众");
if (!t.addCount()){
break;
}
}
}
}
9. 方法重写规则
方法重写
1.存在于父子类之间
2.方法名相同
3.参数列表相同
4.返回值相同 或者是其子类
5.访问权限不能严于父类 /不能窄化父类的访问权限
6.不能比父类抛出更多的异常
7.静态方法可以被继承 但是不能被重写
我们可以通过@Override注解实现正确的重写,此注解可以提高代码的阅读性
package com.atguigu.test6;
public class Pet {
protected String name;
protected int health;
protected int love;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public int getLove() {
return love;
}
public void setLove(int love) {
this.love = love;
}
void print(){
System.out.println("宠物的名字是:" + name + ",健康值是:" + health);
System.out.println("亲密值是:" + love);
}
public Pet(){}
}
package com.atguigu.test6;
public class Dog extends Pet{
private String strain;
public String getStrain(){
return strain;
}
public void setStrain(String strain){
this.strain = strain;
}
public void print(){
System.out.println("狗狗的品种是:" + strain);
}
}
package com.atguigu.test6;
public class TestPet {
public static void main(String[] args) {
Dog dog = new Dog();
dog.print(); // 狗狗的品种是:null
}
}
10. 方法重写和方法重载区别
11.Object类
11.1 重写toString方法
Object类中经常被子类重写的方法
toString() :返回当前对象的信息 包名 + 类名 + hash值
1.为什么Student对象可以直接调用toString方法?
答:因为是从Object类继承而来 所有的类默认继承自Object
2.为什么调用toString方法会出现包名 类名 hash值 ?
答:因为Object类中就是这么实现的
3.为什么要重写toString方法?
答:实际开发中,我们都会重写toString方法 用于输出当前对象的属性名 和 属性值
package com.atguigu.test7;
/**
* Object类中经常被子类重写的方法
* toString():返回当前对象的信息 包名 + 类名 + hash值
*
* 1.为什么Student对象可以直接调用toString方法?
* 答:因为是从Object类继承而来 所有的类默认继承自Object
* 2.为什么调用toString方法会出现包名 类名 hash值?
* 答:因为Object类中就是这么实现的
* */
public class Student {
private String name;
private int age;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
public String toString(){
return "Student[name = " + name + ", age = " + age + "]";
}
public static void main(String[] args) {
Student stu1 = new Student();
stu1.name = "赵四";
stu1.age = 20;
System.out.println(stu1.name);
System.out.println(stu1.age);
// ctrl + 鼠标左键进入源代码
System.out.println(stu1.toString()); // 重写toString前打印 com.atguigu.test7.Student@1540e19d 重写toString后打印 Student[name = 赵四, age = 20]
System.out.println(stu1); // 打印对象地址 相当于调用此对象的toString方法
Student stu2 = new Student();
stu2.name = "大拿";
stu2.age = 21;
System.out.println(stu2);
}
}
11.1 重写equals方法
重写Object类提供的equals方法
1.==和equals的区别?
答:==比较基本数据类型 比较的是值
==比较引用数据类型 比较的是地址
equals方法本身也比较地址 但是我们可以重写自定义比较规则 String类就是对equals进行了重写
2.equals方法本身的作用?
答:父类(Object)中equals方法本身用于比较两个对象的地址是否相同
3.String类使用的equals方法是怎样的效果?
答:String类中对父类Object类中的equals方法进行了重写 将原本的比较地址 重写为了比较内容
package com.atguigu.test8;
/**
* 重写Object类提供的equals方法
*
* 1.==和equals的区别?
* 答:==比较基本数据类型 比较的是值
* ==比较引用数据类型 比较的是地址
* equals方法本身也比较地址 但是我们可以重写自定义比较规则 String类就是equals进行了重写
*
* 2.equals方法本身的作用?
* 答:父类(Object)中equals方法本身用于比较两个对象的地址是否相同
*
* 3.String类使用的equals方法是怎样的效果?
* 答:String类中对父类Object类中的equals方法进行了重写 将原本的比较地址 重写为了比较内容
* */
public class Person {
String name; // 名字
String idCard; // 身份证号
public Person(){}
public Person(String name, String idCard){
this.name = name;
this.idCard = idCard;
}
public static void main(String[] args) {
Person p1 = new Person("赵四", "12392347234235345");
Person p2 = new Person("赵四", "12392347234235345");
System.out.println(p1 == p2); // false
System.out.println(p1.equals(p2)); // false
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1 == str2); // false
System.out.println(str1.equals(str2)); // true
}
}
模拟String类equals方法书写myEquals方法
package com.atguigu.test8;
/**
* 模拟String类equals方法书写myEquals方法
* */
public class MyString {
public static void main(String[] args) {
String str1 = "abc";
char[] ch1 = str1.toCharArray();
for (int i = 0;i < ch1.length;i++){
System.out.print(ch1[i] + "\t"); // a b c
}
System.out.println();
String str2 = "hello world";
String str3 = "hello world";
String str4 = "hello word!";
System.out.println(myEquals(str2, str3)); // true
System.out.println(myEquals(str2, str4)); // false
}
public static boolean myEquals(String str1, String str2){
if (str1 == str2){
return true;
}
char[] ch1 = str1.toCharArray();
char[] ch2 = str2.toCharArray();
if (ch1.length != ch2.length){
return false;
}
for (int i = 0;i < ch1.length;i++){
if (ch1[i] != ch2[i]){
return false;
}
}
return true;
}
}