一、封装
1.概念
将某些东西进行隐藏,通过相应的方式进行获取
封装 (encapsulation)
隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别。
封装途径
封装就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,以特定的访问权限来使用类的成员。
public class Girl {
//属性
private int age;
//读取年龄
public int dqAge(){
return age;
}
//设置年龄
public void setAge(int age){
if(age>=25){
this.age=18;
}
else{
this.age=age;
}
}
}
public class Test {
public static void main(String[] args) {
Girl g=new Girl();
//设置年龄
g.setAge(26);
//读取年龄
System.out.println(g.dqAge());
}
}
2.进行封装
(1)将属性私有化,被private修饰,加入权限修饰符
一旦加入权限修饰符,其他人不可以随意获取这个属性
(2)提供public修饰的方法可以让其他人来访问和使用
(3)即使外界通过方法可以访问属性,但也不能随意访问,通过方法进行限制
3.实际开发中会写成setter,gatter方法
可以利用快捷键alt+enter
public class Girl {
//属性
private int age;
private int height;
private String name;
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
练习
若定义了有参构造器需要将有参构造器中有方法定义重复的部分用方法替代
public class Test {
public static void main(String[] args) {
Student s1=new Student();
s1.setName("lili");
s1.setAge(19);
s1.setSex("女");
System.out.println(s1.getName()+"----"+s1.getAge()+"---"+s1.getSex());
Student s2=new Student("sara",19,"女");
System.out.println(s2.getName()+"----"+s2.getAge()+"---"+s2.getSex());
}
}
package com.twx2;
public class Student {
private String name;
private int age;
private String sex;
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 getSex() {
return sex;
}
public void setSex(String sex) {
if("男".equals(sex)||"女".equals(sex)){
this.sex = sex; //sex是一个男或者是一个女
}
else{
this.sex="男";
}
}
//空参构造器
public Student(){
}
//有参构造器
public Student(String name ,int age,String sex){
this.name=name;
this.age=age;
this.setSex(sex);//重写
}
}
二、继承
类是对对象的抽象
继承是对类的抽象
1.先定义父类(基类,超类)
2.在定义子类(派生类)
子类继承自父类,继承关系是在合理的范围中进行抽取
父类:
public class Person {
//属性
private int age;
private String name;
private int height;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
//方法
public static void eat(){
System.out.println("我喜欢吃寿司");
}
public static void sleep(){
System.out.println("我喜欢在图书馆睡觉");
}
}
子类(用extends进行继承父类)
public class Student extends Person {//子类继承父类
//属性,子类独特的属性
private int ano;//学号
public int getAno() {
return ano;
}
public void setAno(int ano) {
this.ano = ano;
}
//方法
public void study(){
System.out.println("学生可以学习");
}
}
测试类
public class Test {
public static void main(String[] args) {
//创建子类student的对象
Student s=new Student();
s.setName("lili");
s.setAge(19);
s.setHeight(162);
s.setAno(2345);
s.study();
s.eat();
s.sleep();
System.out.println("名字是"+s.getName()+"年龄是"+s.getAge()+"身高"+s.getHeight()+"学号是:"+s.getAno());
}
}
继承的优点:
提高代码的复用性
父类定义的内容可以直接用,不用代码的重复利用
注意:(1)父类private修饰的内容,子类仍然可以继承到,因为封装的特性只是阻碍了直接调用,但是提供了间接调用的方式,可以间接调用。
(2)一个父类可以有多个子类
(3)一个子类只能继承一个父类
(4)继承具有传递性
student extends Person extends object,既可以间接的继承上一个继承的类,所有类都直接或者间接的继承自object;
内存图
三、权限修饰符
1.private
private只能在同一个类中进行访问、使用,若想在其他类使用必须要提供专门的setter、getter方法
2.default(缺省修饰符)
同一个包下的其他的类都可以访问到
3.protected
可以访问到同一个包、同一个类、不同包下的子类
4.public
整个项目都可以使用
总结:
方法、属性:private、default、protected、public。
类:default、public
四、方法的重写
1.重写
发生在子类和父类中,子类对父类提供的方法不能完全使用,就会在子类中进行方法的的重写
2.格式
严格按照父类的格式,包括参数列表(个数,类型,顺序)必须和父类一致。
父类
public class Person {
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
子类
public class Student extends Person {
//方法
public void study(){
System.out.println("学习");
}
public void eat(){//对父类方法的重写
System.out.println("我喜欢晚上吃饭");
}
}
3.重载与重写的区别
重载:在同一个类中,当方法名相同,形参列表不同,多个方法形成了重载
重写:在子类和父类中,子类对父类提供的方法不能满足需求时,在子类中对父类中的方法进行重写,重写时格式完全按照父类中的格式以及形参列表的的个数、顺序,类型;
注意:重载与修饰符与返回值无关,重写中,父类返回值类型必须要大于子类返回值类型,父类修饰符要低于子类的修饰符;
四、super关键字
1.super可以修饰属性和方法
在子类方法中:super.属性;super.方法。显示去调用父类提供的方法和属性,通常状态可以省略。
public void a(){
System.out.println(super.age);//调用父类的方法
eat();
}
在特殊情况下,当父类和子类的属性重名时,要想使用父类的属性,必须加上修饰符super,只能通过super属性来调用。
public void a(){
System.out.println(super.age);//调用父类的方法
this.eat();//调用子类中的方法
super.eat();//调用父类中的方法
}
2.super修饰构造器
(1)空构造器第一行都有默认有super();作用:调用父类的空构造器;
public Student(){//属性赋值
/*super();*/
}
(2)如果在构造器中已经显示的调用super父类构造器,那么第一行就没有默认的super()调用父类构造器
public Student(int age,String name,double score){
//this.age=age;
//this.name=name;
//super.age=age;
//super.name=name;
super(age,name);//调用父类构造器
this.score=score;
//this.score=score;
}
(3)在构造器中,super调用父类构造器和this调用子类构造器只能存在一个,俩个不能共存,super和this修饰构造器都只能在第一行;
public class Student extends Person{
double score;
public Student(){//属性赋值
/*super();*/
}
public Student(double score){
super();//调用父类构造器
this.score=score;
}
public Student(int age,String name,double score){
//this.age=age;
//this.name=name;
//super.age=age;
//super.name=name;
super(age,name);//调用父类构造器
this(score);//二者不可以同时存在
//this.score=score;
}
}
(4)构造器快捷键生成
alt+insert
面试题:
继承条件下构造方法的执行过程
父类
public class Person {
int age;
String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
//空构造器
public Person() {
}
}
子类
public class Student extends Person {
double height;
public Student() {
}
public Student(int age, String name, double height) {
super(age, name);
this.height = height;
}
}
测试类
public class Test {
public static void main(String[] args) {
Student s=new Student(19,"lili",120.1);
}
首先进行类的加载,然后进入到子类构造器中,第一行回到父类构造器,然后进入object类构造器,接着赋值,赋值完成后继续进入子类构造器赋值,赋值完成后,对象创建完成。
五、object类下的方法
1.object类下的toString方法
所有类都直接或间接的继承自object类,object类是所有Java类的根基类,意味着Java对象都拥有object类的属性和方法
(1).object的toString方法
getClass().getName() + ‘@’ + Integer.toHexString(hashCode())
@前面:
全限定路径:包名+类名的完整展示
@后面:hashcode():将对象在堆中进行哈希算法返回一个码,将这个哈希码传入Integer.toHexString(哈希码)中,返回一个字符串,这个字符串以十六进制输出。
public static void main(String[] args) {
//int a=10;
//System.out.println(a);
Student s=new Student(19,"lili",52.5);
System.out.println(s);
//完整表达
System.out.println(s.toString());
//com.twx02.Student@1b6d3586
//com.twx02.Student@1b6d3586
}
(2)将toString()方法进行重写
//toString方法重写
public String toString() {
return "这是一个Student对象,这个对象的名字"+name+",这个对象的年龄"+age+",这个对象的体重"+weight;
}
Test测试
public static void main(String[] args) {
//int a=10;
//System.out.println(a);
Student s=new Student(19,"lili",52.5);
System.out.println(s);
//完整表达
System.out.println(s.toString());
//com.twx02.Student@1b6d3586
//com.twx02.Student@1b6d3586
}
//out:这是一个Student对象,这个对象的名字lili,这个对象的年龄19,这个对象的体重52.5
(3)快捷键生成
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
", weight=" + weight +
'}';
}
//outStudent{age=19, name='lili', weight=52.5}
2.object类下的equals方法
(1)方法提供对对象的内容是否相等的一个比较方式,对象的内容就是属性,父类提供的equals,没有实际意义,需要重写
方法
public class Phone {
//属性
private String brand;//品牌
private double price;//价格
private int year;//日期
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
//方法
//构造器
public Phone() {
}
public Phone(String brand, double price, int year) {
this.brand = brand;
this.price = price;
this.year = year;
}
@Override
public String toString() {
return "Phone{" +
"brand='" + brand + '\'' +
", price=" + price +
", year=" + year +
'}';
}
//equals方法重写
public boolean equals(Object obj) {
//要将obj转化为Phone类型
Phone other = (Phone) obj;//向下转型,为了获取子类中特有的内容。
if (this.getBrand() == other.getBrand() && this.getPrice() == other.getPrice() && this.getYear() == other.getYear()) {
return true;
}
return false;
}
}
测试类
public class Test {
public static void main(String[] args) {
Phone p1=new Phone("huawei",2020.35,2020);
Phone p2=new Phone("huawei",2020.35,2020);
//比较俩个对象
//==的作用,比较等号俩边的数值是否相等,对于俩个基本数据类型
// int a=10,b=10;
// System.out.println(a==b);
System.out.println(p1==p2);//引用数据类型==比较的是地址值,
//object提供equals方法,比较对象具体内容是否相等
boolean flag=p1.equals(p2);
/*
public boolean equals(Object obj) {
return (this == obj);this表示当前调用equals方法的对象,及仍比较的是p1==p2
}
* */
System.out.println(flag);
}
}
(2)equals方法的重写
//equals方法重写
public boolean equals(Object obj) {
//要将obj转化为Phone类型
Phone other = (Phone) obj;
if (this.getBrand() == other.getBrand() && this.getPrice() == other.getPrice() && this.getYear() == other.getYear()) {
return true;
}
return false;
}
(3)方法重写的优化
比较俩个不同类时equals方法如何重写
a instanceof b
a对象是否是b类的实例,是返回ture
Cat c=new Cat();
System.out.println(p1.equals(c));
public boolean equals(Object obj) {
//要将obj转化为Phone类型
Phone other = (Phone) obj;
if(obj instanceof Phone){//obj对象是否是Phone类的实例,是返回true
if (this.getBrand() == other.getBrand() && this.getPrice() == other.getPrice() && this.getYear() == other.getYear()) {
return true;
}
return false;
}
return false;
}
六、类和类的关系
面对对象思维:先找对象
形参:
实参:
类和类之间产生关系
public class Girl {
//属性
int age;
String name;
double weight;
Mom m=new Mom();
//方法
public void love(Boy b){
System.out.println(b.toString());
b.buy();
}
public void weChat(){
m.say();
}
//构造器
public Girl() {
}
public Girl(int age, String name, double weight) {
this.age = age;
this.name = name;
this.weight = weight;
}
}
(1)将一个类作为另一个类中的的方法的形参
public class Boy {
//属性
int age;
String name;
//方法
public void buy(){
System.out.println("buy expensive things");
}
//构造器
public Boy() {
}
public Boy(int age, String name) {
this.age = age;
this.name = name;
}
}
Boy boy=new Boy(19,"武士桑");
Girl girl=new Girl(19,"lili",100);
girl.love(boy);
(2)将一个类作为另一个类的属性
(关系更加密切)
girl.weChat();
七、多态
1.多态与属性无关,多态指的是方法的多态
2.代码
父类
public class Animals {
int age;
public void shout(){
System.out.println("动物可以叫");
}
}
子类
public class Cat extends Animals{
public void shout(){//方法的重载
System.out.println("猫猫喵喵叫");
}
public void stretch(){
System.out.println("猫猫会挠人");
}
}
public class Dog extends Animals {
public void shout(){//方法的重载
System.out.println("狗狗汪汪叫");
}
public void guard(){
System.out.println("狗狗会保护主人");
}
}
public class pig extends Animals {
double weight;
public void shout(){
System.out.println("猪猪哼哼叫");
}
public void eat(){
System.out.println("猪猪吃的很多");
}
}
public class Girl {
/* public static void play(Cat cat){
cat.shout();
cat.stretch();
}
public static void play(Dog dog){
dog.guard();
dog.shout();
}*/
public static void play(Animals an){
an.shout();
}
}
测试类
public class Test {
public static void main(String[] args) {
//创建具体对象
Cat c= new Cat();
Girl g=new Girl();
Dog d=new Dog();
pig p=new pig();
// g.play(c);
//g.play(d);
//Animals an=new Animals();
//Animals an=p;
Animals an=new pig();
g.play(an);
}
}
(1)先有父类再有子类叫做继承,现有子类再有父类叫做泛化
(2)多态:同一个行为不同的子类表现出来的不同形态,多态指的是同一方法的调用,由于对象不同会产生不同的行为
(3)优点:提高代码的扩展性,符合卖你对对象的设计原则:开闭原则(扩展是开放的,修改时关闭的)
(4)多态的要素
a.继承
b.重写:子类对父类的方法shout()重写
c.父类引用指向子类对象
pig p=new pig();
Animals an=new Animals();
Animals an=p;
写为一句话:
Animals an = new pig();
左侧:编译期类型
组测:运行期的类型
d.
public static void play(Animals an){
an.shout();
}
应用场景:父类当作方法的形参然偶传入的时具体的子类的对象,然后调用一个相同的方法,根据传入子类的不同展示出出来的效果也不同,构成了方法
4.内存分析
public static void play(Animals an){
an.shout();
}
5.向上转型,向下转型
父类
public class Animals {
int age;
public void shout(){
System.out.println("动物可以叫");
}
}
子类
public class pig extends Animals {
double weight;
public void shout(){
System.out.println("猪猪哼哼叫");
}
public void eat(){
System.out.println("猪猪吃的很多");
}
}
测试类
public static void main(String[] args) {
pig p=new pig();
Animals an=p;
//Animals an=new pig();
an.shout();
an.age=10;
错误代码
因为父类中没有定义下面的方法和属性
an.eat();
an.weight;
向下转型:
pig pig=(pig)an;//向下转型,将an的类型转换一下
pig.eat();
pig.weight=10;
向上转型
pig p=new pig();
Animals an=p;//向上转型
内存分析
6.简单的工厂设计模式
使用父类做方法的返回值类型,其返回值的对象可以是该类的任意一个子类对象,解决了大量对象创建的问题,将创建和使用分开,工厂负责创建,使用者直接调用要求:
定义一个static方法,通过类名直接创建
返回值类型是父类类型,返回值可以是任意子类类型
传入任意一个类型的参数没根据参数创建对象
工厂:
public class PetStore {
//方法,提取动物
//向上转型
public static Animals getAnimal(String petName){//static方便调用方法,没有static需要创建对象
Animals an=null;//局部变量初始化
if("猫".equals(petName)){//petName.equals("猫")这样写容易发生空指针异常
an=new Cat();
}
if("狗".equals(petName)){
an=new Dog();
}
if("猪".equals(petName)){
an=new pig();
}
return an;
}
}
调用:
Animals an=PetStore.getAnimal("猫");