面向对象的思想
- 首先要区分面向过程和面向对象两种编程思想
- 面向过程---- 上厕所---->去厕所-解裤腰带-排泄-提裤腰带-洗手;
- 面向对象---- 上厕所---->把面向过程中的步骤封装起来形成一个“上厕所”;
- 面向过程的主要特征:
- 封装
- 继承
- 多态
- 面向对象开发就是不断地创建对象,然后调用对象实现功能。
- Java语言其最基本的单元就是类,类是一个抽象数据类型,类中的成员具有某些相同的属性。
- 成员变量和局部变量的区别:
- 成员变量定义在类中方法外,局部变量定义在方法中;
- 成员方法存储在堆中,局部变量存储在栈中;
- 成员方法随对象的创建而存在,随对象的消失而消失,局部变量随方法的存在而存在,随方法的消失而消失;
- 成员变量有默认初始值,局部变量没有。
- 成员变量和局部变量可以同名,在使用时,采用就近原则。
public class MyTest2 {
public static void main(String[] args) {
Student student = new Student();
student.show("王五");
}
}
class Student {
String name="张三";
public void show(String name) {
//当成员变量和局部变量重名时,那么访问变量的规则就遵从就近原则
//就近原则:先从局部范围找,找个变量,找到就使用,如果找不到,去成员位置找,找到就成员
//在这个题中,与这个name最近的是形参列表中的String name,所以将实参name赋给这个输出语句中的name
System.out.println(name);//王五
}
}
对象
- 对象:对象是类的具体实现,如人这个类,具体到某个人如张三,张三就是人这个类的对象。
- 对象的创建:类名 对象名=new 类名();
- 对象的使用:通过实例化的对象,然后对象名.变量名可调用成员变量,对象名.方法名可调用成员方法。
- 演示:
class Test {
int num = 3;
public static void main(String[] args) {
Test t = new Test();//生成了一个Test类的对象t
t.num = 5;//给成员变量num赋值5
method(t);调用method方法,实参为t这个对象
System.out.println(t.num);
}
public static void method(Test t){
t.num = 6;//给t对象的成员变量num赋值6
}
}
程序结果为 6
- 创建对象的内存图解:
匿名对象
- 匿名对象就是没有名字的对象
- 使用场景:仅仅只调用一次的类中的方法的时候,匿名对象可以传递实参
演示:
class Test {
int num = 3;
public static void main(String[] args) {
Test t = new Test();//1、生成了一个Test类的对象t
t.num = 5; //2、给成员变量num赋值5
method(new Test());//3、生成一个匿名对象,相当于新实例了一个对象
System.out.println(t.num);//7、但是这里的t是之前生成的对象,不是匿名对象生成的新对象
}
public static void method(Test t){//4、把刚才匿名对象生成的对象传进来
t.num = 6;//5、给新的对象的成员变量赋值6
}
}
程序结果为 5
封装
- 封装是指隐藏对象的属性和实现细节,对外仅提供公共的访问方式。
- 封装的优点:
- 隐藏了实现的细节;
- 提高了代码的复用性;
- 提高了安全性。
- 封装的原则:
- 把不需要对外提供的内容封装起来;
- 把属性隐藏,对外提供公共的访问方法。
- 使用private关键字可以对成员变量和成员方法进行封装。封装后只能在本类中使用。
- 在使用private对成员变量隐藏后,可以通过get()set()对成员变量赋值
- 演示:
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;
}
}
class Test4{
public static void main(String[] args) {
Student s = new Student();
s.setName("小明");
s.setAge(20);
System.out.println(s.getName()+s.getAge());
}
}
setxxx()用于对私有的成员变量赋值,getxxx()用于返回私有变量的值
this 关键字
- 当成员变量和局部变量同名时,赋值操作的逻辑关系将会变得异常复杂,而this关键字可以解决这个问题。
- this关键字的特点:
- 是当前类的对象的引用,谁调用这个方法,该方法内部的this就代表谁。
- 演示:
public class MyTest2 {
public static void main(String[] args) {
Student student = new Student();
student.show("王五");
}
}
class Student {
String name="张三";
public void show(String name) {
//当成员变量和局部变量重名时,那么访问变量的规则就遵从就近原则
//就近原则:先从局部范围找,找个变量,找到就使用,如果找不到,去成员位置找,找到就成员
//在这个题中,与这个name最近的是形参列表中的String name,所以将实参name赋给这个输出语句中的name
System.out.println(name);//王五
System.out.println(this.name);//张三
}
}
构造方法
- 作用:给对象中的成员赋值。
- 格式:
- 1、方法名与类名相同;
- 2、没有任何返回值,void也没有;
- 构造方法的注意事项:
- 1、如果我们没有定义构造方法,系统会默认生成一个无参构造方法;
- 2、如果我们定义了构造方法,那么系统不会默认生成无参构造,建议我们自己再写上。
- 演示:
class Student {
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public Student(){
this.name="小明";
this.age=20;
}
public static void main(String[] args) {
Student s = new Student();//在这里会加载Student的无参构造方法,然后通过get方法返回成员变量的值
System.out.println(s.getName()+s.getAge());
}
}
- 构造方法的重载
- 与普通方法的重载一样,只需要参数列表不一致即可形成重载(参数类型、参数个数)
class Student {
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public Student(){
}
public Student(String name){
this.name=name;
}
public Student(String name,int age){
this.name=name;
this.age=age;
}
public static void main(String[] args) {
Student s = new Student("小明",20);//会进入public Student(String name,int age)
System.out.println(s.getName()+s.getAge());
}
}
演示:定义一个长方形(RectangleDemo)类,定义求周长(length)和面积(area)的方法,然后定义一个测试类Test,进行测试。
public class RectangleDemo {
private double length;//长
private double weight;//宽
public double getLength() {
return length;
}
public double getWeight() {
return weight;
}
public RectangleDemo(){
}
public RectangleDemo(double length, double weight) {
this.length = length;
this.weight = weight;
}
public double girth(double length,double weight){
double girth=2*(length+weight);
return girth;
}
public double area(double length,double weight){
double area=length*weight;
return area;
}
}
class Test4{
public static void main(String[] args) {
RectangleDemo r=new RectangleDemo(10,20);
double length=r.getLength();
double weight=r.getWeight();
System.out.println("面积是:"+r.area(length,weight));
System.out.println("周长是:"+r.girth(length,weight));
}
}
static 关键字
- 被static(静态的)关键字修饰的变量和方法的特点:
- 二者都存在方法区中随类的加载而加载。
- 优先于对象的存在。
- 被类的所有对象共享。也就是说,如果某个变量是所有对象所共有的,那么它应该是静态的。
- 可以通过类名直接调用,也可以通过对象名调用。
代码块
- 在java中,被{}扩起来的代码称为代码块。
- 代码块的分类:
- 执行顺序为:静态代码块—>构造代码块—>局部代码块
- 静态代码块随类的加载而加载;每调用一次类的构造方法,构造代码块就要执行一次。
- 演示:
class Student {
static {
System.out.println("Student 静态代码块");
}
{
System.out.println("Student 构造代码块");
}
public Student() {
System.out.println("Student 构造方法");
}
}
class StudentDemo {
static {
System.out.println("StudentDemo的静态代码块");
}
public static void main(String[] args) {
System.out.println("我是main方法");
Student s1 = new Student();
Student s2 = new Student();
}
}
程序结果是:
StudentDemo的静态代码块 //main方法在StudentDemo类中,先加载该类,static代码块随类的加载而加载,所以是第一步
我是main方法 //进入main方法后执行该语句
Student 静态代码块 //因为要调用Student类,所以先加载static代码块
Student 构造代码块 //***每调用一次构造方法就要先执行构造代码块***
Student 构造方法 //在生成s1对象时,调用了Student类的无参构造方法,所以输出Student 构造方法
Student 构造代码块 //***每调用一次构造方法就要先执行构造代码块***
Student 构造方法 //在生成s2对象时,调用了Student类的无参构造方法,所以输出Student 构造方法
继承
- 继承概念:如果多个类中存在相同的属性和行为时,那么可以把这些相同的内容抽取到一个类中,该类就是父类,其他类在使用这些功能是只需要继承父类即可,这些类称为子类。
- 继承格式:
- class 子类名 extends 父类名{ }
- 演示:现有学生类和老师类,二者都有吃饭,睡觉的行为,那么我们可以将这两者抽取到一个父类中,然后学生类和老师类继承这个父类即可。
class Student extends Person{
public void doWork(){
System.out.println("写作业!");
}
}
class Teacher extends Person{
public void salary(){
System.out.println("领工资!");
}
}
class Person{
String name;
int age;
char sex;
public void eat(){
System.out.println("吃饭!");
}
public void sleep(){
System.out.println("睡觉!");
}
}
class Test5{
public static void main(String[] args) {
Student student = new Student();
String sname=student.name="小明";
int sage=student.age=20;
char ssex=student.sex='男';//Student继承了Person类,所以可以通过子类实例化后的对象直接访问父类中的成员变量和成员方法
System.out.printf("%s\t%d\t%c\n",sname,sage,ssex);
student.doWork();
student.eat();
student.sleep();
Teacher teacher = new Teacher();
String tname=teacher.name="小王";
int tage=teacher.age=40;
char tsex=teacher.sex='女';
System.out.printf("%s\t%d\t%c\n",tname,tage,tsex);
teacher.salary();
teacher.eat();
teacher.sleep();
}
}
- 继承的优点:
- 提高了代码的重用性;
- 提高了代码的维护性;
- 让类与类之间产生了关系,是多态的前提。
- 继承的缺点:
- 类的耦合性增强了(增强了类与类之间的联系);
- java中继承的特点:
- 支持单继承,不支持多继承;//通俗地讲就是一个儿子只能有一个父亲不能有多个父亲。
- 支持多层继承。//父类可以产生子类,子类还可以再生成子类。
- 子类只能继承父类中非私有地成员变量和成员方法;
- 子类不能继承父类地构造器,但可以通过super关键字去访问;
- 不要为了部分功能去继承。
- 继承中成员变量地关系:
- 当子类中地成员变量名和子类中地成员变量名一样时,采取就近原则;
- 在子类中地局部变量中找,有就使用;
- 在子类地成员范围找,有就使用;
- 在父类中查找。
- 当子类中地成员变量名和子类中地成员变量名一样时,采取就近原则;
- 演示:
class Fu {
public int num = 10;
}
class Zi extends Fu{
public int num = 20;
public void show() {
int num = 30;
System.out.println(num);//就近原则,与num最近地是局部变量 int num
System.out.println(this.num);//this代表引用它地对象,所以是Zi地成员变量20
System.out.println(super.num);//super关键字代表引用父类地,所以是Fu中地成员变量10
}
}
class Test {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
运行结果为
30
20
10
super关键字
- 作用:子类中使用super关键字可以访问父类中地成员变量和方法。
- this和super的区别:
- this代表的是对本类对象的引用;super代表的是对父类的引用;
- this和super的使用:
- this.成员变量 调用本类的成员变量
- super.成员变量 调用父类的成员变量
- this.方法名 调用本类的成员方法
- super.方法名 调用哦个父类的成员方法
- this(—) 调用本类的构造器
- super(—) 调用父类的构造器
- 继承中构造方法的关系:
- 子类继承父类,那么在子类要使用父类的数据前,先要对父类的数据进行初始化,也就是说先要运行父类当中的无参构造方法,此时编译器会默认在子类中的第一句加上super();代码中不会显示。
- 演示:
class Fu {
public int num = 10;
public Fu() {
System.out.println("fu");
}
}
class Zi extends Fu{
public int num = 20;
public Zi() {
System.out.println("zi");
}
public void show() {
int num = 30;
System.out.println(num);
System.out.println(this.num);
System.out.println(super.num);
}
}
class Test {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
运行结果为
fu
zi
30
20
10
分析:因为Zi继承了Fu,那么在初始化Zi前必须先对Fu进行初始化,所以会先执行Fu中的无参构造方法,然后再执行Zi类中的无参构造方法。
- 那么如果父类中如果没有无参构造该怎么办呢?
- 在父类中添加一个无参构造;
- 在子类中通过super调用父类的其他构造方法
- 子类通过this去调用本类的其他构造方法
- 注意:super()和this()语句必须出现在子类中的第一句
- 演示:
class A {
int x = 0;
A(int w){
x = w;
}
}
class B extends A {
int x = 0;
B(int w){
x = w+1;
}
}
class Demo {
public static void main(String[] args){
B b = new B(100);
}
}
分析:该程序将报错,因为在父类A中没有无参构造只有有参构造,而子类构造器中也没有显示调用父类的其他构造器。应该在A类中加入无参构造,或者在子类B中的构造方法的第一句处写super(w);因为我们要对父类先进行初始化,就要先调用它的无参构造,如果没有无参构造,就要在子类中显示调用它的其他构造方法。
- 演示2:
class Fu {
static {
System.out.println("静态代码块Fu");
}
{
System.out.println("构造代码块Fu");
}
public Fu() {
System.out.println("构造方法Fu");
}
}
class Zi extends Fu {
static {
System.out.println("静态代码块Zi");
}
{
System.out.println("构造代码块Zi");
}
public Zi() {
System.out.println("构造方法Zi");
}
}
Zi z = new Zi();
程序结果为:
静态代码块Fu
静态代码块Zi
构造代码块Fu
构造方法Fu
构造代码块Zi
构造方法Zi
分析:Zi类继承了Fu类,那么在对子类进行初始化前,先要对父类进行初始化,而static代码块会随着类的加载而加载,因此,先执行父类中的静态代码块,接下来是子类中的静态代码块;在执行构造方法前,先要执行构造代码块,所以顺序如结果所示。
方法重写
- 当子类需要对父类中的方法需要进行修改时可以使用方法重写。
- 方法重写的格式:子类中出现了和父类中一模一样的方法声明,方法名、参数列表、返回值类型均一致。
- 方法重写需要注意的事项:
- 父类中私有的方法不能重写;
- 被final修饰的方法不能重写;
- 子类重写父类方法时,访问权限不能更低;public>protected>缺省的>private
- 案例演示:
class Fu {
protected void show() {
}
}
class Zi extends Fu {
@Override
protected void show() {
System.out.println("对父类的方法进行了重写");
}
}
final关键字
- final就是最终的意思,可以修饰类、方法、变量。
- final关键字的由来,如果有一个父类中的方法不想让子类重写,那么就可以用final来修饰。
- final的特点:
- 被final修饰的类不能被继承;
- 被final修饰的方法不能被重写;
- 被final修饰的变量不可被修改,即变成了常量。
- 案例演示:
- 我们按住Ctrl+O选择可重写方法时发现,只有work方法可以被重写,被final修饰的方法eat是不能被重写的。