初识Java【4】——类和对象

一、面向对象(Object Oriented)

在编程世界中一直有两种思想:一种是面向过程,另外一种则是面向对象

如各位读者所知:C语言就是一种面向过程(Procedure Oriented)的语言;而C++,Java,还有非常火爆的Python都是面向对象的语言。

当然,这并不是意味着面向过程比面向对象差。面向过程也有自身的优点,在描述比较简单事件的时候,面向过程能够更好的体现出事件发展的时间脉络,步骤非常清晰。而对于面向对象来说,一切皆对象。关注的是:对象本身,以及:对象之间的交互

为了方便各位读者理解,这里举一个经典的例子:把大象塞入冰箱。

  1. 如果是面向过程的话。只需要三步:
    (1)打开冰箱
    (2)把大象塞入
    (3)关上冰箱

用上面这个例子各位读者应该能够很好的理解面向过程了,要理解面向过程就抓住过程的先后顺序

  1. 如果是面向对象的话,首先就是抽取出对象。上例涉及到三个对象:大象、人、冰箱
    (1)大象:具有能够被塞入的能力
    (2)人:A.人能开关冰箱; B.人具有把大象塞入冰箱的能力
    (3)冰箱:A. 冰箱能够被打开 ;B.冰箱具有存储大型事物的能力 ; C.冰箱能够被关闭

这里详细讲讲面向对象。如上所示,我们以及抽取出若干对象了,现在我们要完成 “ 把大象塞入冰箱 ” 这件事就需要 大象、人、冰箱 三者的互相配合
也就是说:作为一个合格的冰箱,冰箱并不需要知道人能干什么,只需要在被打开的时候打开,需要存大象的时候存上,被关上的时候关上就行。只不过这件事情在三者之间的配合中就被完成了,你要是问冰箱,“冰箱,你干了什么?” 它也只会说,“冰箱,干了一个冰箱能干的事。”
总之,要理解面向对象就要抽取出对象,思考对象之间是如何交互

在理解面向对象这种思想之后,我们又如何去利用这种思想编程呢?这里我们引入类的概念。

二、类

1.何为类

是一种用户定义的引用数据类型,也称类类型。类是对现实生活中一类具有共同特征的事物的抽象。在具体编程中,我们用类去实例化一个对象,也就是用类来描述一个对象

2.类的定义与初始化

(1)类的定义

class ClassName {
	field; // 字段、属性、成员变量
	method; // 行为、成员方法
}

类的定义中有以下几点值得注意:

A. 用class + 类名的方式去定义一个类,其中 类名需要用大驼峰的方式定义。
B. 一个Java文件中只能有一个public class。如果这个类 和 主类放在同一个文件夹中,那么就不能使用public修饰。

为了方便对下三个名词进行解释,笔者编写了Aniaml类,请各位读者自行阅读。

class Animal {
	public String name;
	public int age;
	
	public Animal(String name) {
		this.age = age;
		System.out.println("这有一个动物!");
	}
	
	public Animal(String name, int age) {
		this.name = name;
		this.age = age;
		System.out.println("这还有一个动物!");
	}
	
	public void eat() {
		System.out.println("有个动物在吃东西!");
	}
}

1)成员变量
Aniaml类name 、age就是成员变量。也就是说,一个类中定义的一切变量都属于类的成员变量(甚至是在类中再定义的一个内部类,也属于外部类的的成员变量,只不过这个成员变量是类类型罢了)。

2)成员方法
Aniaml类eat()方法就是成员方法。同样的,一个类中定义的一切方法都属于类的成员方法

3)构造方法
这里暂时简单认识一下构造方法,下方类的实例化中会再详细谈一谈构造方法。
Aniaml类Aniaml()就是构造方法,构造方法是一种特殊的成员方法,目前我们可以观察到的有三点:A.构造方法名必须与类名相同,B.构造方法也是可以传参的,C.构造方法能够被重载

(2)类的初始化

类的初始化有三种方式:就地初始化、默认初始化、构造方法初始化。其中第三者方式将在下方 再谈构造方法 的时候详谈。

1)就地初始化

class Animal {
	public String name = "我是动物!";
	public int age = 2;
}

所谓就地初始化即:在类定义之初,当即就为类赋上对应的值

2)默认初始化

class Animal {
	public String name;
	public int age;
}

而默认初始化就是:不用赋任何值。因为:实例化类的成员变量会存储在上,而堆上的变量会被附上该数据类型的默认值。读者可以阅读看完下方的部分内容之后再回来看,这样会好理解一些。

3.类的实例化

上方我们以及知道如何去定义一个类了,那么如何去使用这个类呢?这就涉及到类的实例化

类的实例化就是:用自定义的类类型 去定义一个对象。单纯的文字比较难理解,下面我们结合代码来理解,笔者编写了Dog类辅助说明。

class Dog{
    public String name;
    public int age;

    public void eat() {
    	int teeth = 4;
        System.out.println(teeth + "个牙的狗在吃骨头!");
        this.bark();
    }

    public void bark() {
        System.out.println("狗在汪汪叫!");
    }
}

public class TalkAboutClass {
    public static void main(String[] args) {
        Dog dog1 = new Dog();
        Dog dog2 = new Dog();

        dog1.name = "旺财";
        dog2.name = "来福";
        
        dog1.age = 1;
        dog2.age = 2;
        
        dog1.eat();
        dog1.bark();
    }
}

在上面这段代码中,各位读者需要关注的是Dog dog1 = new Dog(); Dog dog2 = new Dog();这两行。类的实例化就是借助类这个引用类型,去堆上开辟一块空间,这里笔者再给大家做一次分析。

A.Dog是自定义,是我们自定义的引用类型。我们可以根据Dog类定义出无数的引用变量。

B.dog1 和 dog2引用变量,也叫做:引用。引用栈区开辟空间,空间中存储的是一段经过哈希的地址,该地址指向new Dog()后产生的对象

C.new关键字代表在堆上开辟一块连续的内存空间,这块空间用来存储Dog类中的成员变量

D.Dog()是一个没有传参的构造方法。但是我们看到,class Dog中并没有public Dog()的构造方法,具体原因我们待会再谈。

综上所述,要实例化对象就需要遵循类名 + 引用变量名 = new 构造方法的方式

在此之后,我们如何去使用我们定义的成员变量与成员方法呢?各位读者应该也发现了:如果需要访问类中的成员变量,就应该引用变量名.成员变量名的形式去访问;如果需要调用类中的成员方法,就应该引用变量名.成员方法名的形式去调用。

理解完实例化对象之后,我们其实对方法的还有三个疑问:

第一个:为什么在实例化对象之后,堆区上没有存储eat()方法 和 bark()方法
回答:成员方法是存储在方法区

第二个:如果堆区中不存储跟方法有关的信息的话,实例化对象怎么知道自己还有eat()方法 和 bark()方法呢?
因为:创建的对象的时候会创建一个方法栈桢,这个方法栈桢里面存的就是方法区里面对应的方法地址,当实例化对象需要调用方法时,就会去自己的方法栈帧中寻找是否存在该方法的地址

第三个:反过来,类方法中并没有设置被要调用哪个对象,方法又是如何知道为哪个对象调用呢
因为:成员方法是由所需调用的对象的引用去调用,那么对象的引用就可以在虚拟机栈中找到方法的地址

相信对上面三个疑问的解答,能够让各位读者对类在内存上的存储有了更深刻的认识。下方,笔者为示例代码画了一张内存分布图,各位读者可以对照着三个问题的回答再次理解。
在这里插入图片描述

(1)再谈构造方法

如果各位读者足够细心,就会发现:在类的实例化中列举的代码示例没有构造方法。那么是不是代表构造方法没有存在的必要呢?答案是 否定的,因为想要实例化对象就必须使用到构造方法。但是为什么上面的示例可以不写构造方法呢?事实是:如果我们不写构造方法,编译器就会帮我们写一个无参的构造方法,只不过编译器并不会将之显示出来。

class Dog{
	public Dog() {

	}
}

现在各位读者应该能理解为何笔者说:构造方法是一种特殊的成员方法。但其实它还有一个特殊之处:构造方法没有返回类型

有的读者在这里可能就会发问:这是说明构造方法的返回类型是void类型吗?答案是否定的,构造方法没有返回类型返回值为void有极大的差别

返回void时,其实是想告诉编译器:目前没有返回值,但这是可选的,将来未必没有。但是构造方法则是:绝对不返回任何东西,而且你也没有任何选择,于是通过某种特殊的设计将其变为不带任何返回参型(包括void)的方法。

接下来,我们补回 类的初始化 留下来的坑。请各位读者直接阅读下方代码。

class Dog {
	public String name;
	public int age;
	
	public Dog(String name, int age) {
		this.name = name;
		this.age = age;
	}
}
public class TalkAboutClass {
    public static void main(String[] args) {
        Dog dog1 = new Dog("旺财",1);
        Dog dog2 = new Dog("来福",2);
    }
}

通过观察,我们发现用构造方法初始化必须要满足四点:
A.类中必须先定义好需要初始化的成员变量
B.必须在实例化对象时,用构造方法传参
C.构造方法必须自己写,而且参数个数、类型必须与传过来时对应
D.构造方法体中必须有赋值的代码

最后,我们总结一下构造方法的知识点:
A.构造方法名必须与类名相同,且能够传参,也能够被重载
B.调用完构造方法之后,实例化对象才实际产生。如果我们不写构造方法,编译器就会帮我们写一个无参的构造方法;
C.构造方法没有返回类型
D.可以通过构造方法初始化

(2)this关键字

关于this关键字,我们最先要解决的问题是:为什么需要this关键字?我们先看看下面这个构造方法。

class Dog() {
	public String name;
	public int age;

	public void Dog(String name, int age) {
		// 为了方便区分我们把等号左边的name叫做 name[L] 
		name = name;  
		age = age;
	}
}	

我们知道构造方法传参的,那么:当传的参数名跟定义的成员变量名相同的时候,Java应该如何区分两个相同的name呢?name[L]这个变量代表谁?是代表形参?还是代表成员变量?

答案是:name[L]代表的是形参。原因是:局部变量优先

我们通过编译器也可以验证出答案,如果name[L]代表的是成员变量的话,最后控制台打印出来的dog1.name应该是旺财才是正确的,但却打印出null。至于为什么打印出null,这是因为编译器把这当成默认初始化

在这里插入图片描述

如果我们不写把旺财给局部变量,我们应该怎么办呢?这就需要用到this这个关键字了。

解决方法:this.成员变量 = 局部变量(形参)

class Dog() {
	public String name;
	public int age;

	public void Dog(String name, int age) {
		this.name = name;  
		this.age = age;
	}
}	

将代码改成以上形式就正确了,但是为什么添加this关键字就能解决问题呢?

首先要告诉各位读者的是:this关键字代表当前对象的引用

然而,什么是当前对象呢?所谓当前对象就是:在堆区开辟的整块空间。堆区上的整块空间就是一个对象。那么当前对象的引用就是栈区中的引用变量。因此:this.name = name; dog1.name = "旺财";本质上没什么区别。

既然,this关键字代表当前对象的引用,那么用this来调用成员方法也没什么奇怪的。

class Dog {
    public String name;
    public int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
        this.bark();
    }

    public void bark() {
        System.out.println("狗在汪汪叫!");
    }
}

public class TalkAboutClass {
    public static void main(String[] args) {
        Dog dog1 = new Dog("旺财",1);

        System.out.println(dog1.name);
        System.out.println(dog1.age);
    }
}

在这里插入图片描述

除此之外,this关键字还有特殊的一点:this可以调用构造方法。不过通过 this() 调用构造方法需要满足以下三个条件:
A.this()必须在构造方法内部使用
B.必须写在第一行;
C.不要形成闭环(两个构造方法一直相互调用)

class Dog {
    public String name;
    public int age;

    public Dog(String name) {
        this("旺财",1);
        this.name = name;
        System.out.println("在只有一个参数的构造方法中:" + this.name + "是" + this.age + "岁。");
    }

    public Dog(String name, int age) {
        // this("来福"); -- error 形成闭环调用
        this.name = name;
        this.age = age;
        System.out.println("在有两个参数的构造方法中:" + this.name + "是" + this.age + "岁。");
    }
}
public class TalkAboutClass {
    public static void main(String[] args) {
        Dog dog1 = new Dog("来福");
    }
}


这里比较有趣的一点是旺财也是1岁。原因就是:Dog(String name)这个构造方法中,使用this()调用构造方法,让ageDog(String name, int age)这个构造方法中被构造方法初始化了。

(3)static关键字

static是静态关键字,一旦被static修饰,被修饰者将会被存储到方法区中,其生命周期将延长至与整个类相同,而static修饰的变量和方法,在类加载的时候就完成初始化。笔者将分别对 访问成员变量 、 访问成员方法 进行介绍。

A.访问成员变量

在这里插入图片描述

bark1()这个非静态方法中,我们可以看到:无论对象是否被static修饰,都可以轻易访问到。只不过在访问 静态成员变量 的时候,我们需要注意代码的编写方式,最好使用 TestForBlog.teeth的方式,即:类名.静态成员变量

fly1()这个静态方法中,我们却看到编译器给我们报了两个错误。
对于error1,我们可以得知:应该使用类的引用去访问一个 非静态成员变量 ,否则直接访问会报错
对于error2,我们可以得知:静态成员变量不能用引用去访问,因为其不属于类的对象,而属于整个类,所以需要用类名去访问

B.访问成员方法,笔者同样分成同一个类中和不同类中的情况,请各位读者自行阅读下方代码:

在这里插入图片描述

bark1()这个非静态方法中,我们可以看到:无论对象是否被static修饰,同样可以被访问到

fly1()这个静态方法中,我们却看到编译器给我们报了个错误。
在这里插入图片描述
这就说明:我们要在静态方法中访问非静态方法只能先实例化对象。

浅浅总结~
1.非静态方法啥都能访问,访问静态 或 非静态 的 成员变量 与 成员方法 都没毛病!
2.静态方法在访问非静态成员变量 或 非静态成员方法的时候,都需要实例化对象,用对象去访问
3.真正好地访问静态变量、调用静态方法的方式是:类名.成员变量名 或 类名.成员方法名

在了解了上面的知识后,有的读者又联想起刚刚提到的构造方法,想在构造方法中搞搞事情,想要修饰构造方法中的局部变量。

在这里插入图片描述

显而易见,这是不可行的。static修饰的局部变量是属于类本身而不属于类的对象,而构造方法的出现就意味着类的对象的实例化,两者根本是相矛盾的,因此错有所究。

static还有一个重要的知识点:静态方法无法重写,无法也用来实现多态。这个的内容等到介绍多态的时候再给大家详细解释。

最强总结static三大要点:
1.static修饰的成员方法(静态方法)无法访问 非静态成员变量 或 非静态成员方法
2.正确访问静态变量、调用静态方法的方式是:类名.成员变量名 或 类名.成员方法名
3.static不能修饰构造方法中的局部变量
最有灵魂的一句话:static是为类服务的

4.代码块

代码块指:用{ }包起来的这部分代码,代码块一共可以分成四类普通代码块、实例代码块、静态代码块、同步代码块

(1)普通代码块

普通代码块:定义在方法中的代码块

public class TestForBlog {

    public static void main(String[] args) {
        {
            System.out.println("这是普通代码块!");
        }
        System.out.println("这是普通代码块的外部!");
    }
}

(2)实例代码块

实例代码块:定义在类中的代码块(不加修饰符)。实例代码块只有在创建对象时才会执行

class Dog{
    public String name;
    public int age;
    public static int teeth;

    {
        name = "旺财";
    }
}

(3)静态代码块

静态代码块:使用static定义的代码块。一般用于初始化静态成员变量。值得注意的是:静态代码块不管生成多少个对象,其只会执行一次

class Dog{
    public String name;
    public int age;
    public static int teeth;
    static {
        teeth = 4;
    }
}

光谈上面的内容不足以展现出代码块的能耐,请读者自行阅读下方代码,并猜测代码的运行结果。

class People {
    public String name;
    public int age;
    public static int id;

    static {
        id = 1;
        System.out.println("这里是静态代码块!");
    }

    {
        System.out.println("这里是实例化代码块!");
    }
    
    public People(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("这里是构造方法!");
    }

}
public class TestForBlog {
    public static void main(String[] args) {
        People people1 = new People("大白",1);
        System.out.println("===========================");
        People people2 = new People("小黑",2);

    }
}

在这里插入图片描述

程序的执行顺序是非常重要的问题,因此我们需要特别关注这一现象。在查看上方代码执行结果时,我们会很神奇的发现:代码块能在构造方法前执行。然而为什么静态代码块又比实例化代码块优先执行呢?为什么静态代码块只能执行一次呢?

因为静态代码块在类加载阶段就完成了,而一个类只能被加载一次

5.内部类

内部类是定义在类或者方法里面的类。一共可以分成四种内部类:实例内部类(构造内部类)、静态内部类、局部内部类、匿名内部类

class OuterClass {

	class InnerClass {
	
	}
}

那么,我们应该如何在主方法中去访问内部类呢?

OuterClass outerClass = new outerClass():
OuterClass.InnerClass innerClass = outerClass.new InnerClass();

OuterClass.InnerClass不难理解,只需要把内部类当成外部类的成员就行,而成员就是通过.去访问的。

比较难理解的是outerClass.new InnerClass()。很多读者会被这个空格所误导,以为是分成outerClass.newInnerClass()两部分去理解。正好相反,我们是:在外部类的堆区空间中,开辟内部类的空间。因此应该是通过外部类的引用outerClass找到内部空间后,在开辟一块空间new InnerClass()

如果我们理解了标黄的句子,那么上方两行的实例化的代码可以被改成下面这一行:

OuterClass.InnerClass innerClass = new OuterClass().new InnerClass();

(1)实例内部类

A.内部类成员

实例内部类与静态内部类相对,因此我们可以说,实力内部类是:未被static修饰的成员内部类

class OuterClass {
    public int data1 = 1;
    public static int data2 = 2;

    // 实例内部类
    class InnerClass {
        public int data4 = 4;
        public static final int data5 = 5; 
    }
}        

上方这个代码示例中,最值得我们关注的就是public static final int data5 = 5;

部分读者会疑惑:内部类作为外部类的成员,也属于 类的对象,为什么被 static final修饰就可以成功通过编译?
部分读者还会联想到上方提到的:static不能修饰构造方法中的局部变量。那么是不是意味着 构造方法中的局部变量被 static final修饰就能够定义了呢?

首先要回答第二个问题,这是错误的!只要构造方法中的出现static修饰,都是错误的因为static为类服务,只要被它修饰就属于类,哪怕被final修饰后变成常量,也属于类,不属于方法。

至于第一个问题,各位读者应先知道 final修饰局部变量时,这个局部变量会变成常量,而常量在编译阶段就确认了。(这里插一句无关的但又重要的话:一个成员存储在哪里跟final修饰没有关系)我们只能说,内部类非常特殊,作为外部类的成员,自己就是一个类,所以才有static final这种骚操作。

B.外部类与内部类的访问

最关键的只有一句话:实例内部类可以访问任何外部类,外部类在访问实例内部类时,需要先实例化内部类。

这里会遇到一个对于初学者比较棘手的问题:外部类与内部类的成员同名怎么办? 很普遍的解决办法:就近原则,使用内部类的成员

class OuterClass {
    public int data1 = 1;
    public int data2 = 2;

    class InnerClass {
        public int data1 = 11;
        public int data3 = 3;

        public void test() {
            System.out.println(data1);
            System.out.println(data2);
            System.out.println(data3);
        }
    }
}
public class TestForBlog {
    public static void main(String[] args) {
        OuterClass.InnerClass innerClass = new OuterClass().new InnerClass();
        innerClass.test();
    }
}

在这里插入图片描述

这时候一定会有部分读者想问,我就是想访问外部类的同名成员需要怎么办?
笔者也非常想知道这个问题,所以我探究了一下,发现有两种方式。

// 法一:
class OuterClass {
    public int data1 = 1;
    public int data2 = 2;

    class InnerClass {
        public int data1 = 11;
        public int data3 = 3;

        public void test() {
            OuterClass outerClass = new OuterClass();
            System.out.println("外部类data1: " + outerClass.data1);

            System.out.println("内部类data1: " + data1);
            System.out.println(data2);
            System.out.println(data3);
        }
    }
}

在这里插入图片描述

直接在内部类中初始化一个外部类,这样肯定能够访问到外部类中的成员。但这种方法未免显得笨拙不少,于是就有了下面这个方法。

// 法二:
class OuterClass {
    public int data1 = 1;
    public int data2 = 2;

    class InnerClass {
        public int data1 = 11;
        public int data3 = 3;

        public void test() {
            System.out.println("外部类data1: " + OuterClass.this.data1);
            
            System.out.println("内部类data1: " + data1);
            System.out.println(data2);
            System.out.println(data3);
        }
    }
}

在这里插入图片描述

上方这段代码最主要是OuterClass.this.data1怎么理解。其实就是分成OuterClass.thisdata1两个部分。OuterClass.this得到的就是外部类的this,通过外部类的this访问data1,得到的当然是外部类的同名成员变量。

关于实例外部类的总结
1.外部类中的任何成员都可以在实例内部类方法中直接访问(哪怕被private修饰)
2.外部类不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象
3.实例内部类所处的位置与外部类成员位置相同,因此public、private等访问限定符的约束
4.在实例内部类方法中访问同名的成员时,优先访问自己,如果要访问外部类同名的成员,用:外部类名称.this.同名成员来访问;
5. 实例内部类的非静态方法中包含了一个:指向外部类对象的引用外部类名称.this

(2)静态内部类

首先我们最先提出的问题是:如何获取静态内部类 ? 其实很简单,因为访问static修饰者通通遵循:类名.成员的方式。于是就有了下面这行代码:
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();

属于静态内部类的特性就是:不能访问外部类的非静态成员。外部类的非静态成员,需要通过外部类对象的引用才能访问

class OuterClass {
    public int data1 = 1;
    public static int data2 = 2;

    public void test() {
        System.out.println("外部类非静态方法");
    }

    static class InnerClass {
        public int data3 = 3;
        public static int data4 = 4;

        public void func() {
            //System.out.println(data1); // error
            System.out.println(data2);
            System.out.println(data3);
            System.out.println(data4);

            OuterClass outerClass = new OuterClass();
            System.out.println("外部类data1:" + outerClass.data1);
            outerClass.test();

        }
    }
}
public class TestForBlog {
    public static void main(String[] args) {
        OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
        innerClass.func();

    }
}

在这里插入图片描述

(3)局部内部类

局部内部类是指定义在外部类的方法体或者{}中的类。这也意味着:A.只能在方法中调用类; B.不能被public 或 static修饰

(4)匿名内部类

首当其冲的应该是:何为匿名对象。匿名对象指只能使用一次的对象。因此每次使用的时候都要重新new.

匿名内部类需要涉及到接口的知识,所以这里暂且不谈。

public class InnerClass
 {
    public static void main(String[] args) {
        new Person(); 
        // 每次使用的时候都要重新 new
        System.out.println(new Person().age);
        System.out.println(new Person().name);

        // 匿名内部类
        new Person() {

        };
    }
}

结语

类和对象的知识非常繁杂,且非常抽象,我们很难用一两天就理解清楚,在学习的时候要慢慢来,多敲代码感受感受,要自己抽时间回顾知识点。总之,希望各位加油,也希望这篇万字长文对你有所帮助!

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: Spark是一种大数据处理的框架,它可以处理大量的数据并进行分析。初学者可以通过学习Spark的基本概念和使用方法,了解Spark的工作原理和应用场景。在学习Spark的过程中,需要掌握Spark的核心组件和API,例如Spark Core、Spark SQL、Spark Streaming等。此外,还需要学习Spark的部署和调优,以及与其他大数据技术的集成。 ### 回答2: Spark是一种基于内存的分布式计算框架,是大数据处理中最流行的技术之一。Spark简单易用,能够快速地处理海量数据,尤其是在机器学习和数据挖掘领域中表现突出。本文将从初识Spark的角度入手,介绍Spark的基本概念和使用。 一、Spark的基本概念 1. RDD RDD全称为Resilient Distributed Datasets,中文意思是弹性分布式数据集,它是Spark的核心数据结构。RDD是一个不可变的分布式的对象集合,可以跨越多个节点进行并行处理。一个RDD可以分为多个分区,每个分区可以在不同的节点上存储。 2. DAG DAG即Directed Acyclic Graph(有向无环图),它是Spark中的一个概念,用来表示作业的依赖关系。Spark将一个作业拆分成一系列具有依赖关系的任务,每个任务之间的依赖形成了DAG。 3. 窄依赖和宽依赖 对于一个RDD,如果一个子RDD的每个分区只依赖于父RDD的一个分区,这种依赖就称为窄依赖。如果一个子RDD的每个分区依赖于父RDD的多个分区,这种依赖就称为宽依赖。宽依赖会影响Spark的性能,应尽量避免。 二、Spark的使用 1. 安装Spark 要使用Spark,首先需要在本地或者集群上安装Spark。下载安装包解压缩即可,然后设置环境变量,即可在命令行中运行Spark。 2. Spark Shell Spark Shell是Spark的交互式命令行界面,类似于Python的交互式控制台,可以快速测试Spark代码。在命令行中输入spark-shell即可进入。 3. Spark应用程序 除了Spark Shell,Spark还支持以应用程序的形式运行。要创建一个Spark应用程序,可以使用Scala、Java、Python等语言进行编写。使用Spark API,读取数据、处理数据、保存数据等操作都可以通过编写代码完成。 总之,Spark是一种优秀的分布式计算框架,能够在海量数据处理中发挥出强大的作用。初学者可以从掌握RDD、DAG、依赖关系等基本概念开始,逐步深入学习Spark的使用。 ### 回答3: Spark是一种快速、分布式数据处理框架,它能够在成千上万个计算节点之间分配数据和计算任务。Spark的优势在于它支持多种语言和数据源,可以在内存中快速存储和处理数据。 在初学Spark时,我们需要对Spark的架构和核心组件有一些了解。首先,Spark的核心组件是Spark Core,它是一个可以用于建立各种应用程序的计算引擎。与此同时,Spark持有丰富的库,包括Spark SQL、Spark Streaming、MLLib和GraphX等,以支持在各种数据类型(文本、图像、视频、地理定位数据等)上运行各种算法。 若想要在Spark中进行任务,有两种编程API可供选择:Spark的核心API和Spark的SQL及DataFrame API。Spark的核心API基于RDDs(弹性分布式数据集),它是不可变的分布式对象集合,Spark使用RDD来处理、缓存和共享数据。此外,Spark的SQL及DataFrame API提供了更高层次的语言,可以处理结构化和半结构化数据。 除了组件和API之外,我们还需要了解Spark的4个运行模式:本地模式、Standalone模式、YARN模式和Mesos模式。本地模式由单个JVM上单个线程(本地模式)或四个线程(local[*]模式)运行。Standalone通常用于小规模集群或开发和测试环境。在YARN或Mesos模式下,Spark将任务提交给集群管理器,并通过管理器分配和管理资源。 总体来说,初学Spark时,我们需要了解Spark的核心组件、编程API和运行模式。熟悉这些概念以及Spark的架构,可以帮助我们更好地理解Spark和构建高效且可扩展的Spark应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值