【小白自述】
- 过去就听说,到面向对象的时候即使没有女朋友,都可以new好多个,啥时候我也能想new多少new多少?
- 面向对象听了很多老师的课,感觉好绕啊!!!这个类套那个类,怎么套的也是一头雾水!!!怎么才能学好了面向对象嘛???!!!
- 好多人都说面向对象是java的核心,都说工作几年的都不能真正领悟面向对象思想,像我们刚开始学能够掌握吗?要不要放弃?
······
目录
(OOP即object-oriented programming)
作为一个资深的小白翻身专家,今天就给大家倒倒我的面向对象学习史以及一些有趣的学习认知。
初识面向对象
(OOP即object-oriented programming)
与面向对象相对的就是面向过程,接下来我们区分一下面向对象和面向过程。
面向过程特点:
- 步骤清晰简单,第一步、第二步、第三步···只要按照步骤做就能完成
- 面向过程适合处理一些较为简单的问题。如我们想用电脑听歌需要先打开电脑,找到酷狗音乐,选择相应歌曲,点击播放,即完成了电脑听歌的需求。
面向对象特点:
- 物以类聚,分类的思维模式,思考问题的方法是首先解决该问题需要做哪些分类,然后对这些分类进行单独思考。最后才对某个分类下的细节进行面向过程的思索
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题。
- 炒一盘菜,我们考虑食材选择、佐料准备、厨具准备、菜谱等,有这些就能做出来,这是面向对象编程;而具体如何去做,划分到每个细节里是面向过程编程了。
总结:
对于描述复杂事物,为了从宏观上把握,我们要运用面向对象的思想从整体上合理分析整个系统;但具体到微观操作,仍需要用面向过程的思路去处理。
面向对象本质:以类的方式组织代码,以对象的方式封装数据。
从认识角度考虑先有对象后有类。对象是具体的,类时抽象的,类是对象的抽象
从代码运行角度考虑是仙有泪后有对象。类是对象的模板
对象的创建分析
类与对象的关系:
- 类时一种抽象的数据类型,它是对某一类事物整体描述/定义,但并不能代表某一个具体的事物。如:水果、蔬菜、动物
- 对象是抽象概念的具体实例。能够体现出特点,展现出功能的具体实例,而非一个概念。
土豆就是蔬菜类的一个具体实例,狗也是动物类的一个具体实例,苹果也是水果类的具体实例
温馨提示:
如果在观看代码的过程中不懂方法为何如此调用,可以详见:java中方法详解
/**
使用new关键字创建对象
使用new关键字创建是,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及类中构造器的调用
构造器又名构造方法,创建对象时必须调用。
构造器特点:
1、必须和类的名字相同;
2、必须没有返回类型,也不能写void
*/
public class Application{
public static void main(String[] args){
Person p = new Person();
System.out.println("姓名:"+p.name+"证件号:"+p.personID+"性别:"+p.sex+"身高为"+heigt);
p.name = "小李子";
p.personID=101214192830144143L;
p.sex = '男';
p.heigt = 172;
System.out.println("姓名:"+p.name+"证件号:"+p.personID+"性别:"+p.sex+"身高为"+heigt);
p.saySomething();
}
}
/*
类中含属性和功能两部分,可以单独存在。
属性: 字段Field 即成员变量
功能: 成员方法
但记住事物的存在肯定是有它的形态和存在价值的!!!
*/
class Person{
String name;
Long personID;
char sex;
int heigt;
public static void saySomething(){
System.out.print("啊~~~");
}
}
构造器
构造器又名构造方法,创建对象时必须调用。
/*
格式:
修饰符 类名(参数列表){}
*/
public class Student{
//与类名相同,无返回值类型,不写时,系统会默认给出构造器
public Student(){
}
}
特点:
- 必须和类的名字相同;
- 必须没有返回类型,连void也没有
注意:
- 当构造方法未定义时,系统会默认提供无参构造方法
- 一旦定义了有参构造,无参构造必须显示定义
- 构造方法是可以重载的
/*
以下程序报错原因:
使用new关键字,本质实在调用构造器
一旦定义了有参构造,系统就不会自动提供无参构造
如果想使用无参构造,需要重新显示定义无参构造
*/
public class Application{
public static void main(String[] args){
Student st = new Student();// 此时程序会报错
}
}
class Student{
String name;
/*
//如果想要不报错,自己在定义一个无参的构造方法
public Student(){
}
*/
//下面定义一个有参构造
public Student(String name){
this.name = name;
}
}
Alt+insert-->自动生成构造方法
作用:
- 使用new关键字,必须要有构造器
- 实例化初始值
面向对象三大特征
封装
将非直接用的内容存放在盒子里,同时打开一个出口能够获取直接用的内容。即(该露的露,该藏的藏)
程序设计追求“高内聚,低耦合”,
高内聚,指类的内部数据操作细节自己完成,不允许外部干涉;
低耦合,指仅暴露少量的方法给外部使用。
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
将属性私有,使用get/set方法
- 提高程序到 安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 系统可维护性增加
public class Application{
public static void main(String[] args){
Person p = new Person();
System.out.println("姓名:"+p.name+"证件号:"+p.personID+"性别:"+p.sex+"身高为"+heigt);
/*new出的对象不能直接调用私有的Person属性,会报错如:‘name’has private access in 'Person'
如果想要调用Person属性,需要提供get、set方法
p.name = "小李子";
p.personID=101214192830144143L;
p.sex = '男';
p.heigt = 172;
*/
/*
p.setName("小李子");
p.setPersonID(101214192830144143L);
p.setSex('男');
p.setHeight(172);
*/
System.out.println("姓名:"+p.getName()+"证件号:"+p.getPersonID()+"性别:"+p.getSex()+"身高为"+ p.getHeight());
p.saySomething();
}
}
class Person{
//属性私有
private String name;
private Long personID;
private char sex;
private int heigt;
//提供一些可以操作私有属性的方法
//提供一些public的get、set方法
//get获得私有属性的数据
public String getName(){
return this.name;
}
//set给这个数据设置值
public void setName(String name){
this.name = name;
}
public String getPersonID(){
return this.personID;
}
public void setPersonID(Long personID){
this.personID = personID;
}
public String getSex(){
return this.Sex;
}
public void setSex(char sex){
this.sex = sex;
}
public String getHeight(){
return this.height;
}
public void setHeight(int height){
this.height = height;
}
public static void saySomething(){
System.out.print("啊~~~");
}
}
继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
extends:扩展。即自子类是父类的扩展,子类 is one of the 父类
格式:
class 子类 extends 父类{
}
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends表示
java中类只有单继承,没有多继承,可以多层继承
class Person{
String name;
int age;
char sex;
int height;
public static void say(){
}
}
class Student extends Person{
int learningID;
double score;
public static void learning(){
}
}
//父类可以由多个子类继承
class Teacher extends Person{
int teachID;
double salary;
public static void teaching(){
}
}
/*
多层继承演示:
Teenager既具备Student的属性和特征,
也具备Person的属性和特征,
同时还具备object的属性和特征
*/
class Teenager extends Student{
private int age;
public static void setAge(int age){
if(age>19||age<11){
System.out.println("非法青少年");
}else{
this.age = age;
}
}
}
继承是类与类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合
注意:
- 用private修饰的变量和方法是不能被继承的。
- 子类不能继承父类的构造方法,但是可以通过super关键字访问父类构造方法
- 不要为了部分功能而去继承
super关键字:
- super调用父类的构造方法,必须在构造方法的第一行
- super必须只能出现在子类的方法或者构造方法中
- super和this不能同时调用构造方法
this关键字:
- this为了区分类中的成员变量和局部变量
- 当子类中继承的父类中的成员变量和成员方法,实际调用时,既可以用this关键字,也可以用super关键字;
/*
this.name指成员变量
赋值的name是局部变量
*/
public class Student{
private String name;
public static void getName(String name){
this.name = name;
}
}
this VS super:
- 代表的对象不同:
this:代表本类对象的应用
super:代表父类对象的应用
- 前提:
this: 无继承关系(本类中)即可使用
super:只能在继承前提下方(即子类中)可使用
- 构造方法
this() : 本类的构造
super():父类的构造
注意必须要放在构造方法中的第一行
/*
子类中的构造方法会默认调用父类中无参的构造方法
虽然没有直接写super(),但默认有super()
如果父类中没有无参的构造方法,子类的构造方法必须要直接或者间接调用到父类中的构造方法
简而言之,子类中的构造方法必须要用到父类中的构造方法
*/
public class Application{
public static void main(String[] args){
// Person p = new Person();// 打印:我是父类人类的构造方法
// 先打印我是父类人类的构造方法,然后打印我是子类学生类构造方法
Student st = new Student();
}
}
class Person{
/* public Person(){
System.out.println("我是父类人类的构造方法");
}
*/
public Person(String p){
System.out.println("我是父类人类的构造方法");
}
}
class Student extends Person{
public Student(){
super("调用有参构造方法");
System.out.println("我是子类学生类的构造方法");
}
}
思考:为什么子类构造方法必须调用父类构造方法
子类构造方法调用父类构造方法目的是为了完成父类中数据的初始化。
方法重写:
需要有继承关系,子类重写父类的方法
/*
方法的重写一定是仅针对方法,跟属性无关
方法的调用仅和左侧定义的数据类型有关,会根据引用数据类型中的方法决定调用结果
静态方法和非静态方法区别很大
方法的重写只与非静态方法有关系
*/
public class application{
public static void main(String[] args){
Student st = new Student();
st.say();//打印:我是学生类中的静态方法
Person st1 = new Student();//父类的引用指向子类
st1.say();//打印:我是人类中的静态方法
st.cry();//打印:啊~~,我是学生类中的非静态方法
st1.cry(); //打印:啊~~,我是学生类中的非静态方法
st.smile();//哈哈哈,我是人类中的非静态方法
st1.smile();//哈哈哈,我是人类中的非静态方法 哈哈哈,我是学生类中的非静态方法
}
}
class Student extends Person{
public static void say(){
System.out.println("我是学生类中的静态方法");
}
public void cry(){
System.out.println("啊~~,我是学生类中的非静态方法");
}
@Override
public void smile() {
// TODO Auto-generated method stub
super.smile();
System.out.println("哈哈哈,我是学生类中非静态方法");
}
}
class Person{
public static void say(){
System.out.println("我是人类中的静态方法");
}
public void cry(){
System.out.println("我是人类中的非静态方法");
}
public void smile() {
System.out.println("哈哈哈,我是人类中的非静态方法");
}
}
- 方法名必须相同,方法体不同
- 参数列表必须相同>
- 修饰符:范围可以扩大但不能缩小; public>protected>default>private
- 抛出异常:范围可以被缩小但不能扩大:ClassNotFoundException-->Exception(大)
思考:为什么要重写?
父类的功能,子类不一定需要,活着不一定满足
继承中成员变量的用法
继承中的成员变量的关系:
- 先在子类中找,子类中如果没有就去父类中找,父类中如果没有就报错
public Application{
public static void main(String[] args){
Student st = new Student();
System.out.println(st.country);//打印结果为 USA,如果Student类中没有,则为China
st.show("India");//打印结果依次为India、USA、China
}
}
class Person{
String name;
int age;
double weight;
String country = "China";
public void say(){
}
}
class Student extends Person{
String name;
int grade;
int score;
String country = "USA";
public void test(){
}
public void show(String country){
System.out.println(country);//就近原则
System.out.println(this.country);//this关键字的应用指向成员变量
System.out.println(super.country);//super关键字的应用指向父类中的成员变量
}
}
- 父类对象无法调用子类中的成员变量
多态
public class application{
public static void main(String[] args){
Student st = new Student();
Student.say();//打印:我是学生类中的静态方法
Person st1 = new Student();//父类的引用指向子类
Person.say();//打印:我是人类中的静态方法
st.cry();//打印:啊~~,我是学生类中的非静态方法
/*
父类不可以调子类中独有的方法,如果想要调用需要强制转换((Student)st1).cry()
st1.cry(); //报错:ClassCastException
*/
st.smile();//哈哈哈,我是人类中的非静态方法
st1.smile();//哈哈哈,我是人类中的非静态方法 哈哈哈,我是学生类中的非静态方法
}
}
class Student extends Person{
//含static的方法是静态的方法,类名直接调用,尽量不适用对象调用
public static void say(){
System.out.println("我是学生类中的静态方法");
}
public void cry(){
System.out.println("啊~~,我是学生类中的非静态方法");
}
@Override
public void smile() {
// TODO Auto-generated method stub
super.smile();
System.out.println("哈哈哈,我是学生类中非静态方法");
}
}
class Person{
//含static的方法是静态的方法,类名直接调用,尽量不适用对象调用
public static void say(){
System.out.println("我是人类中的静态方法");
}
public void smile() {
System.out.println("哈哈哈,我是人类中的非静态方法");
}
}
多态是方法的多态,属性没有多态
父类和子类有继承关系,如果想让父类的对象调用子类的方法,需要进行强制转换
容易出现类型转换异常 ClassCastException
存在条件:继承关系,方法需要重写@Override,
父类引用指向子类对象 Person p= new Student();
instanceof (类型转换)引用类型 ,判断一个对象是什么类型
//如果A与B存在继承关系,那么结果为true,否则为false。
System.out.println(A instanceof B);
子类继承父类,向上转型直接转,向下转型需要用强转,过程中可能会损失精度。
public class Student{
public static void main(String[] args){
Student st = new Student();
/* 执行结果依次为:
我是静态代码块
我是匿名代码块
我是构造代码块
通过打印结果我们能够得出执行速度:静态代码块>匿名代码块>构造代码块
*/
Student st1 = new Student();
/* 执行结果依次为:
我是匿名代码块
我是构造代码块
静态代码块只执行1次
*/
}
//代码块(匿名代码块)可以赋初始值
{
System.out.println("我是匿名代码块");
}
//静态代码块
static{
System.out.println("我是静态代码块");
}
//构造方法
public Student(){
System.out.prinln("我是构造方法");
}
}
执行速度:静态代码块>匿名代码块>构造代码块
抽象类和接口
抽象类:单继承
表现形式:
/*
抽象类也是单继承
格式:public abstract class 类名{}
抽象类不能new只能靠子类去实现它:约束
抽象方法必须在抽象类中
抽象类中可以写普通的方法
*/
public abstract class Description{
/*
抽象方法须有关键字abstract
抽象方法只有方法名没有方法实现,需要被实现,因此引入继承
*/
public abstract void sayStory();
}
思考: 抽象类中有构造器吗?
无法创建对象,那是没有构造器的。
抽象类存在的意义是什么?
抽象出来,提高开发效率
接口
约束和实现分离:面向接口编程
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是~~则必须能做~~”
接口的本质是契约,就想法律一样,一旦制定,在大环境下都需要遵守
面向对象(OO)的精髓,是对对象的抽象,最能体现这一点的是接口。
- 只有规范方法,自己无法写方法~专业的约束!
- 类可以实现接口,
- 表现形式:class A implements interface B
- 只要实现了接口的类就需要重写接口中的方法
- 接口是可以实现多继承的
/*
类可以实现接口,表现形式:A implements B
只要实现了接口的类就需要重写接口中的方法
接口是可以实现多继承的
*/
public class UserImpl implements UserService,TimeService{
public void add(String name) {
// TODO Auto-generated method stub
}
public void delete(String name) {
// TODO Auto-generated method stub
}
public void update(String name) {
// TODO Auto-generated method stub
}
public void quary(String name) {
// TODO Auto-generated method stub
}
public void timer() {
// TODO Auto-generated method stub
}
}
//接口都需要有实现类,定义的关键字是interface
public interface UserService{
//静态常量 public static final可以不写
public static final int AGE = 15;
//接口中所有定义其实都是抽象的 public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
public interface TimeService{
void timer();
}
内部类及面向对象实战
内部类概念
在一个类的内部再定义一个类,如A类中定义一个B类,那么B类相对A类来说就成为内部类
class OutClass{
System.out.println("我是外部类");
class InnerClass{
System.out.println("我是内部类");
}
}
内部类分类
- 成员内部类
/**
成员内部类的演示效果
可以获得外部类的私有属性和私有方法
调用前要先通过外部类实例化内部类
*/
class ApplicationTest{
public static void main(String[] args){
//测试是否可以new内部类和外部类,如何保障正常调用
OuterDemo od = new OuterDemo();
//通过这个外部类来实例化内部类~
OuterDemo.InnerDemo id = od.new InnerDemo();
id.getID();//得到的打印结果为1012;
}
}
public class OuterDemo{
private int id = 1012;
public void out(){
System.out.println("这是外部类的方法");
}
public class InnerDemo{
public void in(){
System.out.println("这是内部类的方法");
}
//获得外部类的私有属性
public void getID(){
System.out.println(id);
}
}
}
- 静态内部类
/**
静态内部类的演示效果
不可以获得外部类的私有属性和私有方法,因为static为内存分配,在没有创建对象前已经存在
*/
class ApplicationTest{
public static void main(String[] args){
//测试是否可以new内部类和外部类,如何保障正常调用
OuterDemo od = new OuterDemo();
//通过这个外部类来实例化内部类~
OuterDemo.InnerDemo id = od.new InnerDemo();
// id.getID();//报错,static内存直接分配;
}
}
public class OuterDemo{
private int id = 1012;
public void out(){
System.out.println("这是外部类的方法");
}
public static class InnerDemo{
public void in(){
System.out.println("这是内部类的方法");
}
/*不能获得外部类的私有属性
public void getID(){
System.out.println(id);
}
*/
}
}
- 局部内部类
/**
局部内部类的演示效果
*/
public class ApplicationTest{
public static void main(String[] args){
OuterDemo od = new OuterDemo();
}
}
public class OuterDemo{
//局部内部类
public void method(){
class InnerDemo{
public void in(){
}
}
}
}
- 匿名内部类
/**
匿名内部类的展示效果
*/
public class Test{
public static void main(String[] args){
//没有名字初始化类,不用将实例保存到变量中
new NameClass().myName();
}
}
class NameClass{
public void myName(){
System.out.println("我的名字叫测试匿名内部类");
}
}
致歉:
因工作比较忙,更新的不够及时,望小伙伴们海涵。
作品会及时更新,更多内容也可以观看www.51doit.cn