面向过程就是分析出解决问题所需要的步骤,然后用方法把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
而具体的每一步都需要我们去实现和操作。这些步骤相互调用和协作,完成我们的需求。在上面的每一个具体步骤中我们都是参与者,并且需要面对具体的每一个步骤和过程,这就是面向过程最直接的体现。
那么什么是面向过程开发呢? 面向过程开发,其实就是面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。
面向过程的代表语言:C语言
当需求单一,或者简单时,我们一步一步去操作没问题。可随着需求的更改,功能的增多,发现需要面对每一个步骤很麻烦了。这时就我们开始思索,能不能把这些步骤和功能再进行封装,封装时根据不同的功能,进行不同的封装,功能类似的封装在一起。这样结构就清晰了很多。用的时候,找到对应的类就可以了。
面向对象就是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
那么什么是面向对象开发呢?面向对象开发就是不断地创建对象,使用对象,指挥对象做事情,从而完成需求。
(1)是一种更符合我们思想习惯的思想
(2)可以将复杂的事情简单化
(3)将我们从执行者变成了指挥者,角色发生了转换
在生活中,面向过程的思想就好比手洗衣服,首先需要接盆水,然后把衣服放进去,再倒上洗衣液,然后用手揉搓衣服,再漂净衣服,最后晾干。这其中的每一步都需要我们一步步实现,每一步我们都是参与者。
而面向对象,则是我们可以买一台全自动洗衣机,只需要按下按键即可完成洗衣服(调用类中方法)。
1.封装
封装性是面向对象编程的核心思想
指的就是将描述某种实体的数据和基于这些数的操作集合到一起,形成一个封装体
封装的思想保证了类内部数据结构的完整性,使用户无法轻易直接操作类的内部数据,这样降低了对内部数据的影响,提高了程序的安全性和可维护性。
2.继承
继承是Java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或类从父类继承方法,使得子类具有父类相同的行为。
3.多态
多态指同一个实体同时具有多种形式。它是面向对象程序设计(OOP)的一个重要特征。
接口的多种不同的实现方式即为多态。
这个现实世界是由事物组成。生活中我们如何描述现实世界事物?
举例: 描述学生
属性:姓名 , 年龄 , 性别 …
行为:学习 , 吃饭 , 睡觉 …
属性: 就是该事物的描述信息
行为: 就是该事物能够做什么
编程语言,就是为了模拟现实中的事物,我们学习的Java语言最基本单位是类,所以,我们就应该把事物用一个类来体现。
由此我们就得到了现实中的事物和类的对应关系
事物 ----------------- 类
属性 ----------------- 成员变量
行为 ----------------- 成员方法
定义类其实就是定义类的成员(成员变量和成员方法)
(1)成员变量 和以前定义变量是一样的,只不过位置发生了改变。在类中,方法外。
(2)成员方法 和以前定义方法是一样的,只不过把static去掉,后面在详细讲解static的作用。
类和对象的概念
(1)类:是一组相关的属性和行为的集合
(2)对象:是该类事物的具体体现
(3)举例:
类——学生
对象——班长
1.学生类
定义一个学生类
public class Student {
//属性——————成员变量
//成员变量要定义在类中方法外
String name;//可以给属性赋初值,不赋值的情况下为该数据类型的默认值,此处为null
int age;//默认值0
char sex;//默认值空字符
//行为——————成员方法
public void study(){
System.out.println("正在学习中...");
}
public void eat(){
System.out.println("正在吃饭中...");
}
public void sleep(){
System.out.println("正在睡觉中...");
}
}
同一个包下,定义一个测试类创建对象并调用类属性和功能
public class MyTest {
public static void main(String[] args) {
//定义一个类,体现的就是封装的思想---封装了成员变量和成员方法
//定义一个类使用关键字 class
//我要定义一个学生类
//类是一个抽象的概念,你无法直接使用他的属性和功能,我们要使用该类,那必须要对类进行实例化
//所谓实例化,就是创建该类的对象。如何创建一个类的对象呢?使用关键字new
//创建该类的实例(对象)
Student st = new Student();
//设置属性的值
st.name="张三";
st.age=18;
st.sex='男';
//有了对象以后,通过对象调用类中的属性和功能
//获取属性 对象名.属性名
String name=st.name;
int age=st.age;
char sex=st.sex;
System.out.println(name);
System.out.println(age);
System.out.println(sex);
//调用方法 对象名.方法名()
st.eat();
st.sleep();
st.study();
System.out.println("------------------------------------");
//一个类可以创建很多对象
Student st2 = new Student();
st2.name="小红";
st2.age=19;
st2.sex='女';
System.out.println(st2.name);
System.out.println(st2.age);
System.out.println(st2.sex);
st2.eat();
st2.sleep();
st2.study();
}
}
public class Teacher {
String name;
int age;
public void teach(){
System.out.println("正在教课中...");
}
public void sleep(){
System.out.println("正在休息中...");
}
}
同一个包下,定义一个测试类创建对象并调用类属性和功能
public class MyTest {
public static void main(String[] args) {
Teacher t1 = new Teacher();
t1.name = "张三";
t1.age = 26;
System.out.println(t1.name);
System.out.println(t1.age);
Teacher t2 = new Teacher();
t2.name = "赵四";
t2.age = 27;
Teacher t3=t1;
t3.name="田七";
t1.age=30;
System.out.println(t1.name);
System.out.println(t1.age);
System.out.println(t2.name);
System.out.println(t2.age);
System.out.println(t3.name);
System.out.println(t3.age);
}
}
内存解析
首先.class文件被加载进方法区,main方法被调用进栈,执行到第一句代码时,堆内存为t1对象开辟空间,并赋了默认值,再将空间的地址赋给t1这个引用,因此t1便指向了该空间。执行到第二、三句代码,便通过t1找到空间,将name改为“张三”,age改为26,此时的输出就为“张三”和26。再执行下面的代码,堆内存为t2对象开辟空间并初始化默认值,再将空间的地址赋给t2这个引用,接着再将name改为“赵四”,age改为27。然后,将t1这个引用赋给t3,t3并没有new,所以没有开辟新的空间,而是指向了t1指向的空间,因此t1和t3共同操作一个空间。最后将t3.name改为“田七”,也即改变了t1.name,又将t1.age改为30,于是后面的输出便为“田七”,30,“赵四”,27,“田七”,30。
注意事项:局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则。
变量的访问原则——就近原则:方法中要访问一个变量,会先在局部位置找,找到就使用;如果找不到,会找成员变量,找到就使用 。
案例演示
1.
/*
一个Java文件中,可以定义多个类,
注意:public只能有一个,在有main方法的类前面加上,其他类不能加public
*/
public class MyTest {
public static void main(String[] args) {
Cat c = new Cat();
c.test("汤姆");
}
}
/*
变量的访问原则——就近原则:方法中要访问一个变量,会先在局部位置找,找到就使用
如果找不到,会找成员变量,找到就使用
*/
class Cat{
//成员变量
String name="发财";
//方法内部的变量和形参都属于局部变量
public void test(String name){
System.out.println(name);
}
}
此时test方法中需要输出name,先找局部变量,发现形参名字就为name,于是输出汤姆,而不会先去找成员变量。
而将代码改一下:
public class MyTest {
public static void main(String[] args) {
Cat c = new Cat();
c.test("汤姆");
}
}
class Cat{
//成员变量
String name="发财";
//方法内部的变量和形参都属于局部变量
public void test(String name1){
String name="杰克";
System.out.println(name);
}
}
此时test方法中需要输出name,先找局部变量,发现方法内部的变量name,于是输出杰克,而不会先去找成员变量。
2.
public class MyTest {
public static void main(String[] args) {
//基本数据类型,作为参数传递,形参的改变,不影响实参
//引用输入类型,作为参数传递,形参的改变,会影响实参
//引用数据类型:数组,类,接口
Dog dog = new Dog();
String name = "小白";
int age = 3;
//如果看到一个方法的形参,数据类型是类,那么就传一个该类的对象
dog.show(dog, name, age);
System.out.println(dog.name);
System.out.println(dog.age);
System.out.println(name);
System.out.println(age);
}
}
class Dog {
String name;
int age;
public void show(Dog d, String name, int age) {
name = "汪汪";
age = 2;
d.name = name;
d.age = age;
System.out.println(name);
System.out.println(age);
}
}
匿名对象:就是没有名字的对象,是对象的一种简化表示形式
public class MyTest {
public static void main(String[] args) {
//普通对象调用成员变量及方法
Cat c = new Cat();
c.age=3;
int age = c.age;
System.out.println(age);
c.eat();
//匿名对象调用成员变量及方法
int age1=new Cat().age;//匿名对象调用成员变量,每new一次就产生一个新的对象
System.out.println(age1);
new Cat().eat();//匿名对象的方法调用,每new一次就产生一个新的对象
}
}
class Cat{
int age=2;
public void eat(){
System.out.println("吃鱼");
}
}
new Cat().eat();
new Cat().eat();
这是2个Cat类的对象分别调用了eat()方法,而不是一个对象调用了两次方法。
public class MyTest {
public static void main(String[] args) {
Cat cat = new Cat();
//如果看到一个方法的形参的数据类型为类,那么就传一个该类的对象
cat.show(cat, 19);
System.out.println(cat.num);
System.out.println("---------------------");
//匿名对象可以作为参数传递
cat.show(new Cat(), 20);
System.out.println(cat.num);
}
}
class Cat {
int num = 100;
public void show(Cat cat, int num) {
cat.num = num;
System.out.println(cat.num);
}
}
先来看一个例子:
public class Person {
String name;
int age;
public void show() {
System.out.println(name);
System.out.println(age);
}
}
public class MyTest {
public static void main(String[] args) {
Person p = new Person();
p.name="张三";
p.age=-10;
p.show();
}
}
本例通过 对象名.成员变量名=值
这种方式给成员变量设置值,但是不能排除一些不合理的数据,就比如年龄不可能为-10岁,我们希望能够对给成员变量设置的数据进行一些校验,那么我们就可以屏蔽掉 对象名.成员变量名=值
这种设置方式。
那么怎么屏蔽掉呢?可以使用关键字 private
。
对比:public是一个权限修饰符,可以修饰类,可以修饰成员变量和成员方法,被修饰的成员在任何地方都可以访问。
案例演示
public class Person {
String name;
//private 是一个权限修饰符,私有的,可以修饰成员变量、成员方法,被修饰的成员,只能在本类中访问,外界无法访问。
private int age;//加上了private权限修饰符,私有成员变量
public void show() {
System.out.println(name);
System.out.println(age);
}
}
同理,如果成员方法前面的权限修饰符不是public而是private,那么该方法也无法在其他类中被调用。
成员变量被隐藏起来了无法被外界访问了,那么我们如何给创建的对象设置属性值呢?
这时我们就可以在定义的类中提供公共的访问方法,从而修改对象的属性。
public class Person {
String name;
private int age;//加上了private权限修饰符,私有成员变量
public void show() {
System.out.println(name);
System.out.println(age);
}
public void setName(String n) {//通过公共的set方法设置其属性
name = n;
}
public void setAge(int a) {//通过公共的set方法设置其属性
if(a>=0&&a<=120){//对数据进行校验
age = a;
}
else{
System.out.println("输入年龄不合逻辑!");
}
}
}
public class MyTest {
public static void main(String[] args) {
Person p = new Person();
p.name="张三";//依旧可以访问到name这个成员变量。
//p.age=-10;无法直接访问被private修饰的属性
p.setAge(10);//但可以通过公共的set方法设置其属性
p.show();
System.out.println("--------------");
Person p1 = new Person();
p1.setName("赵四");
p1.setAge(-10);
p1.show();
}
}
现在我们解决了设置私有属性的值的问题,那么假设我们需要获取对象的私有属性又该怎么办呢?
同样地,我们可以在类中提供公共的访问方式,返回该对象的属性值即可。
public class Person {
String name;
private int age;//加上了private权限修饰符,私有成员变量
public void setName(String n) {
name = n;
}
public void setAge(int a) {
if(a>=0&&a<=120){
age= a;
}
else{
System.out.println("输入年龄不合逻辑!");
}
}
public String getName() {//返回name
return name;
}
public int getAge() {//返回age
return age;
}
}
public class MyTest {
public static void main(String[] args) {
Person p = new Person();
p.name="张三";//依旧可以访问到name这个成员变量。
//p.age=-10;无法直接访问被private修饰的属性
p.setAge(10);//但可以通过公共的set方法设置其属性
String name=p.getName();//获取对象p的name
int age=p.getAge();////获取对象p的age
System.out.println(name+" "+age);
Person p1 = new Person();
p1.setName("赵四");
p1.setAge(20);
String name1=p1.getName();
int age1=p1.getAge();
System.out.println(name1+" "+age1);
}
}
是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。
将不需要对外提供的内容都隐藏起来。
把属性隐藏,提供公共方法对其访问。
先来看一个例子:
假如set方法中,局部变量(形参)和成员变量同名了,那么当该set方法被调用时还会改变对象的属性值吗?
public class Person {
String name;
private int age;
public void setName(String name) {
name = name;
}
public void setAge(int age) {
age= age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void show(){
System.out.println(name+" "+age);
}
}
public class MyTest {
public static void main(String[] args) {
Person p = new Person();
p.setName("张三");
p.setAge(10);
p.show();
}
}
从结果我们发现输出并不是张三和10,证明该对象的属性并没有被修改,仍然是默认值。这是因为“就近原则”,set方法中的两个name都是指的局部变量(形参)的那个name,也即局部变量name自己赋给自己,并没有改变成员变量name,因此我们需要一个关键字this
来解决此问题:
public class Person {
String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age= age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void show(){
System.out.println(name+" "+age);
}
}
public class MyTest {
public static void main(String[] args) {
Person p = new Person();
p.setName("张三");//p调用了set方法,那么set方法中的this就代表p。
p.setAge(10);
p.show();
}
}
当我们的局部变量和成员变量重名的时候,如果我们不使用this关键字,那么会导致一个问题:就是局部变量隐藏了成员变量的问题
是当前类的对象引用。
简单的记,它就代表当前类的一个对象。谁调用这个方法,那么该方法的内部的this就代表谁
public class MyTest {
public static void main(String[] args) {
A a = new A();
System.out.println(a);//a的地址
int num=10;
a.show(num,a);
System.out.println(num); //就近原则
}
}
class A{
int num=100;
public void show(int num, A a){
System.out.println(this);//与a的地址相同
System.out.println(num);//就近原则
System.out.println(a.num);
System.out.println(this.num);
System.out.println(a==this);//this实际上就相当于正调用该方法的对象
}
}
假设有一个类A,那么创建一个A类对象:A a=new A();
,这个A()
其实是一个方法,叫做构造方法。
构造方法的重载
public class Student {
String name;
int age;
//构造方法:方法名和类名相同,没有返回值类型,连void 也没有
//我们自定义的一个类中,即使不写构造方法,默认也会存在一个空参的构造方法
//构造方法,就是用来初始化这个类的,完成对类的实例化
public Student(){
System.out.println("无参构造方法执行了");
}
public Student(String name){//构造方法的重载
this.name=name;
System.out.println("一个参数的构造方法执行了");
}
public Student(String name,int age){//构造方法的重载
this.name=name;
this.age=age;
System.out.println("两个参数的构造方法执行了");
}
}
public class MyTest {
public static void main(String[] args) {
//类,是一个抽象的概念,不能直接使用,要使用类中的属性和功能,必须对类进行实例化(创建对象)
//我们在创建对象时,除了使用关键字new之外,还得借助构造方法,来完成对类的实例化。
//借助空参的构造方法创建对象
Student st1 = new Student();
System.out.println(st1.name);
System.out.println(st1.age);
//借助有参的构造方法创建对象
Student st2 = new Student("张三");
System.out.println(st2.name);
System.out.println(st2.age);
Student st3 = new Student("赵四", 20);
System.out.println(st3.name);
System.out.println(st3.age);
}
}
如下:
此时即使Dog类中没有写构造方法,创建Dog类的对象时也不会报错,因为默认存在一个无参构造方法。
但是如果我们给出了有参构造方法,那么默认的无参构造方法就没有了,此时若用无参构造方法创建对象会报错。
如果你还想要使用无参构造来创建对象,建议你手动写出来,也建议你永远手动写出来。
案例演示
public class Teacher {
//一个类的组成:构造方法、成员变量、成员方法
//私有成员变量
private String name;
private int age;
//提供有参、无参构造
public Teacher() {
}
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
//提供公共的 get set 方法
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 class MyTest {
public static void main(String[] args) {
//使用空参构造来创建对象
Teacher teacher = new Teacher();
//set方法给成员变量赋值
teacher.setName("张三");
teacher.setAge(18);
System.out.println(teacher.getName());
System.out.println(teacher.getAge());
//使用有参构造来创建对象,在创建对象的同时,就可以给成员变量赋值
Teacher teacher1 = new Teacher("李四", 35);
System.out.println(teacher1.getName());
System.out.println(teacher1.getAge());
}
}
(1)加载.class文件进内存
(2)在栈内存为创建对象的变量开辟空间
(3)在堆内存为对象开辟空间
(4)给对象的成员变量进行默认初始化
(5)给对象的成员变量进行显式初始化
(6)通过构造方法给对象的成员变量赋值
(7)对象初始化完毕,把对象地址赋值给变量
案例演示
public class Student {
private String name="张三";
private int age=10;
public Student(String name, int age) {
this.name = name;
this.age = 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 class MyTest {
public static void main(String[] args) {
Student s = new Student("王五", 25);
//(1)加载Student.class文件进内存
//(2)在栈内存为s开辟空间
//(3)在堆内存为学生对象开辟空间
//(4)对学生对象的成员变量进行默认初始化
//(5)对学生对象的成员变量进行显式初始化
//(6)通过构造方法对学生对象的成员变量赋值
//(7)学生对象初始化完毕,把对象地址赋值给s变量
}
}
练习:
定义一个长方形(Rectangle)类,定义求周长和求面积的方法,然后定义一个测试类,进行测试。
public class Rectangle {
private double length;
private double width;
public Rectangle(double length,double width){//有参构造
this.length=length;
this.width=width;
}
public double getPerimeter(){//求周长
return 2*length+2*width;
}
public double getArea(){//求面积
return length*width;
}
}
public class RectangleDemo {
public static void main(String[] args){
Rectangle rectangle = new Rectangle(2.5, 4.2);
System.out.println("周长为:"+rectangle.getPerimeter());
System.out.println("面积为:"+rectangle.getArea());
}
}
案例
public class Person {
public String name;
//我想定义一个全是中国人的类
//我想让中国这个数据变成共享变量,让这个类的所有对象,都能够共享它
//我们可以使用一个关键字,static 静态的 可以修饰成员变量和成员方法
//被static所修饰的成员变量,是一个共享变量,被类的所有对象所共享
//被static所修饰的成员叫作静态成员,它随着类的加载而加载(随着.class文件加载进内存,静态成员就会加载)
//静态成员优先于对象而存在
public static String country = "中国";
}
public class MyTest {
public static void main(String[] args) {
Person person = new Person();
person.name="张三";
person.country="美国";
Person person2 = new Person();
person2.name = "李四";
person2.country = "新加坡";//因为静态成员变量共享,所以修改它,每个对象的该成员变量同时改变
System.out.println(person.name+"----"+person.country);
System.out.println(person2.name + "----" + person2.country);
}
}
注意:类一加载,静态成员就存在了。(静态成员随着类的加载而加载)
(1)随着类的加载而加载
(2)优先于对象存在
(3)被类的所有对象共享
举例:一个班级的学生应该共用同一个班级编号。
其实这个特点也是在告诉我们什么时候使用静态
如果某个成员变量是被所有对象共享的,那么它就应该定义为静态的。
(4)可以通过类名调用
其实它本身也可以通过对象名调用,但推荐使用类名调用。
静态修饰的内容一般我们称其为:与类相关的、类成员
案例演示
public class MyTest {
public static void main(String[] args) {
//A a = new A();
//int b = a.b;
//通过 对象名.共享变量名 这种方式,去访问共享变量不报错,但不推荐使用
//被static 所修饰的我们推荐使用类名直接调用
//因为静态变量,属于类,所以使用类名直接调用即可
A.b=500;
int b = A.b;
System.out.println(b);
//a.show(); 不推荐使用
A.show();//静态方法,推荐使用类名直接调用
}
}
class A{
int a=100;
static int b=1000; //静态变量(共享变量)
public static void show(){//静态方法
System.out.println("这是一个静态的show方法");
}
}
(1)在静态方法中是没有this关键字的
如何理解呢?
静态是随着类的加载而加载的,this是随着对象的创建而存在的。
因此,静态比对象先存在。
(2)静态方法只能访问静态成员变量和静态成员方法;而非静态方法不仅可以访问静态成员变量和静态成员方法,还能访问非静态成员变量和非静态成员方法。
简单记:静态只能访问静态,非静态可以访问静态的也可以访问非静态的。
案例演示
public class MyTest {
public static void main(String[] args) {
}
}
class B{
int a=10;
static double num=100;
//非静态方法,既可以访问静态变量,也可以访问非静态变量
public void show(){
System.out.println(a);
System.out.println(num);
}
//静态方法
public static void staticShow(){
//System.out.println(a);报错,不能访问非静态变量
System.out.println(num);
}
//非静态方法,既可以访问静态方法,也可以访问非静态方法
public void test() {
this.show();
staticShow();
}
//静态方法,只能访问静态方法
public static void test2() {
//show();报错
//this.staticShow();报错
//静态方法里面,不能存在this,this代表一个对象,对象是后来才有的,先有的,不能访问后有的
staticShow();
}
}
注意:main方法也是静态方法,main方法中调用的方法也必须得是静态方法,否则无法直接调用。
(1)所属不同
静态变量属于类,所以也称为类变量
成员变量属于对象,所以也称为实例变量(对象变量)
(2)内存中位置不同
静态变量存储于方法区中的静态区
成员变量存储于堆内存
(3)内存出现时间不同
静态变量随着类的加载而加载,随着类的消失而消失
成员变量随着对象的创建而存在,随着对象的消失而消失
(4)调用不同
静态变量可以通过类名调用,也可以通过对象调用(不推荐)
成员变量只能通过对象名调用
Math类包含用于执行基本数学运算的方法
由于Math类在java.lang包下,所以不需要导包。
没有构造方法,因为它的成员全部是静态的(可以直接用类调用方法,不需要创建对象)。
public static double random();
返回带正号的 double 值,该值大于等于 0.0 且小于 1.0。
案例演示
获取十个1-100之间的随机整数(包括1和100)
public class MyTest {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
//直接用类调用方法
int num=(int)(Math.random()*100+1);//强制转换为整型
System.out.println(num);
}
}
}