Sala详解---------类


Scala中的类

摘要:

在本篇中,你将会学习如何用Scala实现类。如果你了解Java或C++中的类,你不会觉得这有多难,并且你会很享受Scala更加精简的表示法带来的便利。本篇的要点包括:

1. 类中的字段自动带有getter方法和setter方法

2. 你可以用定制的getter/setter方法替换掉字段的定义,而不必修改使用类的客户端,这就是所谓的"统一访问原则"

3. 用@BeanProperty注解来生成JavaBeans的getXxx/setXxx()方法

4. 每个类都有一个主要的构造器,这个构造器和类定义"交织"在一起。它的参数直接成为类的字段。主构造器执行类体中所有的语句

5. 辅助构造器是可选的,它们叫做this


简单类和无参方法

简单类

Scala类最简单的形式看上去和Java或c+++的很相似:

class Counter {

 private var value=0 //你必须初始化字段

 def increment() {value+=1} //方法默认是公有的

 def current()=value

}

在Scala中,类并不声明为public。Scala源文件可以包含多个类,所有这些类都具有公有可见性。使用该类需要做的就是构造对象并按照通常的方式来调用方法:

val myCounter=new Counter // 或new Counter()

myCounter.increment()

println (myCounter.current) // 1

无参方法

调用无参方法比如current时,你可以写上圆括号,也可以不写:

myCounter.current //OK

myCounter.current() //同样OK

应该用哪一种形式呢,我们认为对于改值器方法,即改变对象状态的方法使用(),而对于取值器方法不会改变对象状态的方法去掉()是不错的风格。这也是我们在示例中的做法:

myCounter.increment() //对改值器使用()

println (myCounter.current) //对取值器不使用()

你可以通过以不带()的方式声明current来强制这种风格:

class Counter {

 def current=value //定义中不带()

}

这样一来类的使用者就必须用myComter.current,不带圆括号。


带getter和setter的属性

Java中的字段属性

编写Java类时,我们并不喜欢使用公有字段:

public class Person{ // 这是Java

 public int age; //Java中不鼓励这样做

}

使用公有字段的话,任何人都可以写入fred.age,让Fred更年轻或更老。这就是为什么我们更倾向于使用getter和setter方法:

public class Person{ //这是Java

  private int age;

  public int getAge() { return age; }

  public void setAge{int age) { this.age=age; }

}

像这样的一对getter/setter通常被称做属性(property),我们会说Person类有一个age属性。这到底好在哪里呢,仅从它自身来说,并不比公有字段来得更好。任何人都可以调用fred.setAge(21),让他永远停留在21岁。不过如果这是个问题,我们可以防止它发生:

public void setAge (nt newValue) {// 不能变年轻

 if ( newValue>age )

 age=newValue;

}

之所以说getter和setter方法比公有字段更好,是因为它们让你可以从简单的get/set机制出发,并在需要的时候做改进。需要注意的是:仅仅因为getter和setter方法比公有字段更好,并不意味着它们总是好的。通常,如果每个客户瑞都可以对一个对象的状态数据进行获取和设置,这明显是很糟糕的。下面,会向展示如何用Scala实现属性。但要靠你自己决定,可以取值和改值的字段是否是合理的设计

Scala中的字段属性

Scala对每个字端都提供getter和setter方法。在这里,我们定义一个公有字段:

class Person {

  var age=0

}

Scala生成面向JVM的类,其中有一个私有的age字段以及相应的getter方法和setter方法。这两个方法是公有的,因为我们没有将age声明为private。而对私有字段而言,getter和setter方法也是私有的。

Scala中的getter和setter

在Scala中,getter和setter分别叫做age和age_=例如:

println (fred.age) // 将调用方fred.age()

fred.age= 21 // 将调用fred.age=(21)

如果想亲眼看到这些方法,可以编译Person类,然后用javap查看字节码:

scalac Person.scala

javap -private Person

Compiled from "Person.scala"

public class Person extends java.lang.Object implements scala.ScalaObject {

  private int age;

  public int age()

  public void age_$eq(int)

  public Person()

}

正如你看到的那样,编译器创建了age和age_$eq方法。=号被翻译成$eq,是因为JVM不允许在方法名中出现=

说明:在Scala中,getter和setter方法并非被命名为getXxx和setXxx,不过它们的用意是相同的。后面会介绍如何生成Java风格的getXxx和setXxx方法,以使得你的Scala类可以与Java工具实现互操作

Scala中的自定义getter和setter

在任何时候你都可以自己重新定义getter和setter方法。例如:

class Person {

  private var privateAge =0 // 变成私有并改名

  def age = privateAge

  def age_= (newValue: Int) {

  if (newValue > privateAge)

   privateAge=newValue // 不能变年轻

  }

}

你的类的使用者仍然可以访问fred.age,但现在Fred不能变年轻了:

fred.age = 30

fred.age = 21

println (fred.age) // 30

颇具影响的Eiffel语言的发明者Bertrand Meyer提出了统一访问原则,内容如下:"某个模块提供的所有服务都应该能通过统一的表示法访问到,至于它们是通过存储还是通过计算来实现的,从访问方式上应无从获知"。在Scala中,fred.age的调用者并不知道age是通过字段还是通过方法来实现的。当然了,在JVM中,该服务总是通过方法来实现的,要么是编译器合成,要么由程序员提供。

还需注意的是:Scala对每个字段生成getter和setter方法听上去有些恐怖,不过你可以控制这个过程如下:

■ 如果字段是私有的,则getter和setter方法也是私有的

■ 如果字段是val,则只有getter方法被生成

■ 如果你不需要任何getter或setter,可以将字段声明为private[this]


只带getter的属性

Scala类中的常量

有时候你需要一个只读属性,有getter但没有setter。如果属性的值在对象构建完成后就不再改变,则可以使用val字段:

class Message {

 val timeStamp=new java.util.Date

 ……

}

Scala会生成一个私有的final字段和一个getter方法,但没有setter。

私有字段的getter和setter

不过,有时你需要这样一个属性,客户端不能随意改值,但它可以通过某种其他的方式被改变。前面中的Counter类就是个很好的例子,从概念上讲,counter有一个current属性,当increment方法被调用时更新,但并没有对应的setter

class Counter {

 private var value=0 //你必须初始化字段

 def increment() {value+=1} //方法默认是公有的

 def current()=value

}

需要注意的是,你不能通过val来实现这样一个属性,val永不改变。你需要提供一个私有字段和一个属性的getter方法,像这样:

class Counter {

 private var value=0 //你必须初始化字段

 def increment() {value+=1} //方法默认是公有的

 def current=value //声明中没有()

}

在getter方法的定义中并没有(),因此,你必须以不带圆括号的方式来调用:

  val n=myCounter.current // myCounter.current()这样的调用方式是语法错误

总结

总结一下,在实现属性时你有如下四个选择:

  ■ var foo: Scala自动合成一个getter和一个setter

  ■ val foo: Scala自动合成一个getter

  ■ 由你来定义foo和foo_=方法

  ■ 由你来定义foo方法

但在Scala中,你不能实现只写属性,即带有setter但不带getter的属性。当你在Scala类中看到字段的时候,记住它和Java或c++中的字段不同。它是一个私有字段,加上getter方法(对val字段而言)或者getter和setter了法(对var字段而言)


对私有字段

类私有字段

在Scala中Java和C++也一样,方法可以访问该类的所有对象的私有字段。例如:

class Counter {

 private var value=0

 def increment () {value+=1 }

 def isLess (other: Counter) = value < other.value // 可以访问另一个对象的私有字段

}

之所以访问other.value是合法的,是因为othert也同样是Cormter对象。

对象私有字段

除此之外Scala允许我们定义更加严格的访问限制,通过private[this]这个修饰符来实现:

  private [this] var value=0 // 类似某个对象.value这样的访问将不被允许

这样一来,Counter类的方法只能访问到当前对象的value字段,而不能访问同样是Counter类型的其他对象的该字段。这样的访问有时被称为对象私有的,这在某些OO语言,比如SmaIITalk中十分常见。对于类私有的字段,Scala生成私有的getter和setter方法。但对于对象私有的字段,Scala根本不会生成getter或setter方法。

权限指定

Scala允许你将访问权赋予指定的类,private[类名]修饰符可以定义仅有指定类的方法可以访问给定的字段。这里的类名必须是当前定义的类,或者是包含该类的外部类。在这种情况下,编译器会生成辅助的getter相setter方法,允许外部类访问该字段。这些类将会是公有的,因为JVM并没有更细粒度的访问控制系统,并且它们的名称也会随着JVM实现不同而不同。


Bean属性

正如你在前面所看到的,Scala对于你定义的字段提供了getter和setter方法。不过,这些方法的名称并不是Java工具所预期的。JavaBeans规范

www.oracle.com/technetwork/java/javase/tech/index-jsp-138795.html

把Java属性定义为一对getFoo/setFoo方法或者对于只读属性而言单个getFoo方法。许多Java工具都依赖这样的命名习惯。当你将Scala字段标注为@BeanProperty时,这样的方法会自动生成。例如:

import scala.reflect.BeanProperty

class Person {

   @BeanProperty var name: String=_

}

将会生成四个方法:

  ■ name:String

  ■ name_=(newValue: Strmg):Unit

  ■ getName():String

  ■ setName(newValue: String): Unit

下表显示了在各种情况下哪些方法会被生成:

如果你以主构造器参数的方式定义了某字段,并且你需要JavaBeans版的getter和setter方法,像如下这样给构造器参数加上注解即可:

class Person (@BeanProperty var name: String)


辅助构造器

和Java或C++一样,Scala可以有任意多的构造器。不过Scala类有一个构造器比其他所有构造器都更为重要,它就是主构造器(primary constructor)。除了主构造器之外,类还可以有任意多的辅助构造器( auxiliary constructor)我们将首先讨论辅助构造器,这是因为它们更容易理解。它们同Java或C++的构造器十分相似,只有两处不同。

 ■ 辅助构造器的名称为this。在Java或C++中,构造器的名称和类名相同,当你修改类名时就不那么方便了

 ■ 每一个辅助构造器都必须以一个对先前已定义的其他辅助构造器或主构造器的调用开始

这里有一个带有两个辅助构造器的类。

class Person {

 private var name=""

 private var age=0

 def this(name: String){ //一个辅助构造器

  this() // 调用主构造器

  this.name=name

 }

 def this (name: String,age: Int) { // 另一个辅助构造器

  this (name) //调用前一个辅助构造器

  this.age=age

 }

}

和Java、C++一一样,类如果没有显式定义主构造器则自动拥有一个无参的主构造器即可。你可以以三种方式构建对象:

val p1 = new Person //主构造器

val p2 = new Person("Fred")//第一个辅助构造器

val p3 = new Person ("Fred",42) //第二个辅助构造器


主构造器

主构造器的参数直接放置在类名之后

在Scala中,每个类都有主构造器。主构造器并不以this方法定义,而是与类定义交织在一起

class Person (val name:String, val aqe:Int)
{

  // (…)中的内容就是主构造器的参数

}

主构造器的参数被编译成字段,其值被初始化成构造时传入的参数。在本例中name和age成为Person类的字段。如new Person("Fred",42)这样的构造器调用将设置name和age字段。我们只用半行Scala就完成了七行Java代码的工作:

public class Person{ //这是Java

private String name;

private int age;

public Person(String name,int age) {

 this.name=name

 this.age=age

}

public String name() {return this.name;}

 public int age() {raturn this.age;}

}

主构造器会执行类定义中的所有语句。例如在以下类中:

class Person (val name: String, val age: Int) {

 println ("Just constructed anther person")

 def description=name+"is"+age+"years old"

}

println语句是主构造器的一部分。每当有对象被构造出来时,上述代码就会被执行。当你需要在构造过程当中配置某个字段时这个特性特别有用。例如:

class MyProg {

 private val props=new Properties

 props.load ( new  FileReader ( "myprog.properties" ) )  // 上述语句是主构造器的一部分

}

类名之后没有参数

如果类名之后没有参数,则该类具备一个无参主构造器。这样一个构造器仅仅是简单地执行类体中的所有语句而已。你通常可以通过在主构造器中使用默认参数来避免过多地使用辅助构造器。例如:

  class Person (val name:String="",val age: Int =0 )

主构造器参数

主构造器的参数可以采用下表中列出的任意形态

例如:

  class Person (val name : String, privite var age: Int)

这段代码将声明并初始化如下字段:

  val name: String

  private var age: Int

构造参数也可以是普通的方法参数,不带val或var,这样的参数如何处理取决于它们在类中如何被使用。如果不带val或var的参数至少被一个方法所使用,它将被升格为字段。例如:

class Person(name: String, age: Int) {

  def description=name+"is"+age+"years old"

}

上述代码声明并初始化了不可变字段name和age,而这两个字段都是对象私有的。类似这样的字段等同于private[this] val字段的效果。否则,该参数将不被保存为字段。它仅仅是一个可以被主构造器中的代码访问的普通参数。严格地说,这是一个具体实现相关的优化。

主构造器参数生成字段

下表总结了不同类型的主构造器参数对应会生成的字段和方法:

如果主构造器的表示法让你困惑,你不需要使用它。你只要按照常规的做法提供一个或多个辅助构造器即可,不过要记得调用this(),如果你不和其他辅助构造器串接的话。话虽如此,许多程序员还是喜欢主构造器这种精简的写法。Martin Odersky建议这样来看待主构造器:在Scala中,类也接受参数,就像方法一样。当你把主构造器的参数看做是类参数时,不带val或var的参数就变得易于理解了,这样的参数的作用域涵盖了整个类。因此,你可以在方法中使用它们。而一旦你这样做了,编译器就自动帮你将它保存为字段。

类定义与主构造器

Scala设计者们认为每敲一个键都是珍贵的,因此他们让你可以把类定义和主构造器结合在一起。当你阅读一个Scala类时,你需要将它们分开。举例来说,当你看到如下代码时:

class Person (val name: String)  {

 var age=0

 def description=name+"is"+age+"years old"

}

把它拆开成一个类定义:

class Person (val name: String)  {

  var age = 0

  def description = name+"is"+age+"years old"

}

和一个构造器定义:

class Person(val name: String) {

  var age = 0

  daf description= nama+"is"+age+"years old"

}

如果想让主构造器变成私有的,可以像这样放置private关键字:

  class Person private ( val id: Int ) { … }

这样一来类用户就必须通过辅助构造器来构造Person对象了


嵌套类

Scala内嵌类

在Scala中,你几乎可以在任何语法结构中内嵌任何语法结构。你可以在函数中定义函数,在类中定义类。以下代码是在类中定义类的一个示例:

import scala.collection.mutable.ArrayBuffer

class Network {

  class Member(val name: String) {

   val contacts = new ArrayBuffer[Member]

  }

  private val members=new ArrayBuffer[Member]

  def join(name: String) ={

    val m=new Member(name)

    members+=m

    m

  }

}

在Scala中,每个实例都有它自己的Member类,就和它们有自己的members字段一样,考虑有如下两个网络:

val chatter = new Network

val myFace = new Network

也就是说,chatter.Member和myFace.Member是不同的两个类。这和Java不同,在Java中内部类从属于外部类。Scala采用的方式更符合常规,举例来说,要构建一个新的内部对象,你只需要简单的new这个类名:new
chatter.Member。而在Java中,你需要使用一个特殊语法:chatter.new Member()。拿我们的网络示例来讲,你可以在各自的网络中添加成员,但不能跨网添加成员:

val fred =
chatter.join("Fred")

val wilma=chatter.join ("Wilma")

fred.contacts+=wilma //OK

val barney=myFace.join ("Barney") // 类型为myFace .Member

fred.contacts+=barney // 不可以这样做,不能将一个myFace.Member添加到chatter.Member元素缓冲当中

Scala内嵌类访问

对于社交网络而言,这样的行为是讲得通的。如果你不希望是这个效果,有两种解决方式。首先,你可以将Member类移到别处,一个不错的位置是Network的伴生对象。

object Network {

  class Member (val name: String) {

   val contacts=new ArrayBuffer[Member]

  }

}

class Network{

  private val members = new ArrayBuffer[Network.Member]

}

或者,你也可以使用类型投影Network#Member,其含义是"任何Network的Member"。例如:

class Network {

  class Member (val name: String)  {

    val contacts = new ArrayBuffer[Network#Member]

  }

}

如果你只想在某些地方,而不是所有地方,利用这个细粒度的"每个对象有自己的内部类"的特性,则可以考虑使用类型投影。

内嵌类访问外部类

在内嵌类中,你可以通过外部类.this的方式来访问外部类的this引用,就像Java那样。如果你觉得需要,也可以用如下语法建立一个指向该引用的别名:☆☆

class Network(val name: String){ outer=>

  class Member (val name: String) {

    def dascription=name+"inside"+outer.name

  }

}

class Network { outer=>语法使得outer变量指向Network.this。对这个变量,你可以用任何合法的名称。self这个名称很常见,但用在嵌套类中可能会引发歧义。这样的语法和"自身类型"语法相关,将会后面内容继续介绍

类与对象

类是一个对象的蓝图。一旦定义一个类可以创建从类蓝图使用关键字new创建对象。下面是一个简单的语法在Scala中定义一个类:

class Point(xc: Int, yc: Int) {
   var x: Int = xc
   var y: Int = yc

   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("Point x location : " + x);
      println ("Point y location : " + y);
   }
}

这个类定义了两个变量x和y和方法:move,没有返回值。类变量被调用,类的字段和方法被称为类方法。

类名可以作为一个类的构造函数,可以采取一些参数。上面的代码定义了两个构造函数的参数:xc和yc;它们都在类的主体内可见。

正如前面提到的,可以使用关键字new创建对象,然后可以按照下面的例子所示访问类的字段和方法:

import java.io._

class Point(val xc: Int, val yc: Int) {
   var x: Int = xc
   var y: Int = yc
   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("Point x location : " + x);
      println ("Point y location : " + y);
   }
}

object Test {
   def main(args: Array[String]) {
      val pt = new Point(10, 20);

      // Move to a new location
      pt.move(10, 10);
   }
}

当上述代码被编译和执行时,它产生了以下结果:

C:/>scalac Test.scala
C:/>scala Test
Point x location : 20
Point y location : 30

C:/>

扩展一个类:

可以扩展scala类以类似的方式,如在Java中的一样,但有两个限制:方法重载需要override关键字,只有主构造可以传递参数给基构造。现在扩展上面的类,并增加一个类的方法:

class Point(val xc: Int, val yc: Int) {
   var x: Int = xc
   var y: Int = yc
   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("Point x location : " + x);
      println ("Point y location : " + y);
   }
}

class Location(override val xc: Int, override val yc: Int,
   val zc :Int) extends Point(xc, yc){
   var z: Int = zc

   def move(dx: Int, dy: Int, dz: Int) {
      x = x + dx
      y = y + dy
      z = z + dz
      println ("Point x location : " + x);
      println ("Point y location : " + y);
      println ("Point z location : " + z);
   }
}

extends子句有两种作用:它使类Location继承类Point所有非私有成员,它使Location类作为Point类的子类。 因此,这里的Point类称为超类,而Location类被称为子类。扩展一个类,继承父类的所有功能,被称为继承,但scala允许继承,只能从一个唯一的类。让我们看看完整的例子,显示继承的用法:

import java.io._

class Point(val xc: Int, val yc: Int) {
   var x: Int = xc
   var y: Int = yc
   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("Point x location : " + x);
      println ("Point y location : " + y);
   }
}

class Location(override val xc: Int, override val yc: Int,
   val zc :Int) extends Point(xc, yc){
   var z: Int = zc

   def move(dx: Int, dy: Int, dz: Int) {
      x = x + dx
      y = y + dy
      z = z + dz
      println ("Point x location : " + x);
      println ("Point y location : " + y);
      println ("Point z location : " + z);
   }
}

object Test {
   def main(args: Array[String]) {
      val loc = new Location(10, 20, 15);

      // Move to a new location
      loc.move(10, 10, 5);
   }
}

需要注意的是方法move,不会覆盖 move 方法相应的定义,因为它们是不同的定义(例如,前两个参数,而后者则需要三个参数)。

让我们编译和运行上面的程序,这将产生以下结果:

C:/>scalac Test.scala
C:/>scala Test
Point x location : 20
Point y location : 30
Point z location : 20

C:/>

单例对象:

Scala比Java更面向对象,因为在Scala中不能有静态成员。相反,Scala有单例的对象。单例就是只能有一个实例,即,类的对象。可以使用关键字object代替class关键字,而不是创建单例。因为不能实例化一个单独的对象,不能将参数传递给主构造。前面已经看到全部采用单一对象,调用Scala的main方法的例子。以下是单例显示的相同的例子:

import java.io._

class Point(val xc: Int, val yc: Int) {
   var x: Int = xc
   var y: Int = yc
   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
   }
}

object Test {
   def main(args: Array[String]) {
      val point = new Point(10, 20)
      printPoint

      def printPoint{
         println ("Point x location : " + point.x);
         println ("Point y location : " + point.y);
      }
   }
}

当上述代码被编译和执行时,它产生了以下结果:

C:/>scalac Test.scala
C:/>scala Test
Point x location : 10
Point y location : 20

C:/>

本文参考文章

http://www.cnblogs.com/sunddenly/p/4427551.html

http://www.yiibai.com/scala/scala_classes_objects.html

版权声明:欢迎转载,希望在你转载的同时,添加原文地址,谢谢配合

时间: 2024-11-07 07:23:09

Sala详解---------类的相关文章

设计模式之适配器模式 adapter 适配器模式分类概念角色详解 类适配器 对象适配器 接口适配器 双向适配器

现实世界中的适配器模型 先来看下来几个图片,截图自淘宝 上图为港版的插头与港版的插座 上图为插座适配器卖家的描述图 上图为适配后的结果 现实世界中适配器模式 角色分类 这就是适配器模式在电源插座上的应用 我们看下在插座适配器中的几个重要角色 可以看得出来,大陆和港版插座面板,都是作为电源的角色,他们的功能是相似的或者说相近的 插头要使用插座,进而接通电流 现实世界到代码的转换 电源插座代码示例 港版插座面板 package adapter; /**目标角色 Target 接口 * 香港地区使用的

JAVA正则表达式:Pattern类与Matcher类详解(转)

java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包.它包括两个类:Pattern和Matcher Pattern 一个Pattern是一个正则表达式经编译后的表现模式. Matcher 一个Matcher对象是一个状态机器,它依据Pattern对象做为匹配模式对字符串展开匹配检查. 首先一个Pattern实例订制了一个所用语法与PERL的类似的正则表达式经编译后的模式,然后一个Matcher实例在这个给定的Pattern实例的模式控制下进行字符串的匹配工作

CSS系列(7)CSS类选择器Class详解

这一篇文章,以笔记形式写. 1,  CSS 类选择器详解 http://www.w3school.com.cn/css/css_selector_class.asp 知识点: (1)    使用类选择器的前提是给标签添加上类属性,比如<p class="important"></p> (2)    类选择器的语法为:*.important {color:red;},不过一般省略前面的通配符选择器,写成 .important {color:red;},这样就会给所

详解C# Tuple VS ValueTuple(元组类 VS 值元组)

C# 7.0已经出来一段时间了,大家都知道新特性里面有个对元组的优化:ValueTuple.这里利用详尽的例子详解Tuple VS ValueTuple(元组类VS值元组),10分钟让你更了解ValueTuple的好处和用法. 如果您对Tuple足够了解,可以直接跳过章节"回顾Tuple",直达章节"ValueTuple详解",查看值元组的炫丽用法. 回顾Tuple Tuple是C# 4.0时出的新特性,.Net Framework 4.0以上版本可用. 元组是一种

QAction类详解:

先贴一段描述:Qt文档原文: Detailed Description The QAction class provides an abstract user interface action that can be inserted into widgets. In applications many common commands can be invoked via menus, toolbar buttons, and keyboard shortcuts. Since the user

Android技术18:Android中Adapter类详解

1.Adapter设计模式 Android中adapter接口有很多种实现,例如,ArrayAdapter,BaseAdapter,CursorAdapter,SimpleAdapter,SimpleCursorAdapter等,他们分别对应不同的数据源.例如,ArrayAdater对应List和数组数据源,而CursorAdapter对应Cursor对象(一般从数据库中获取的记录集).这些Adapter都需要getView方法返回当前列表项显示的View对象.当Model发生改变时,会调用Ba

CSS中伪类及伪元素用法详解

原文:CSS中伪类及伪元素用法详解 伪类的分类及作用: 注:该表引自W3School教程 伪元素的分类及作用: 接下来让博主通过一些生动的实例(之前的作业或小作品)来说明几种常用伪类的用法和效果,其他的读者可以自己尝试: :active  大致效果为用鼠标点击时,元素增加特效,鼠标松开时,特效消失.多用在按钮的点击上. 写法: 这里id为box的是一div块,在css中首先设置了他的基本样式,下面为加入:active伪类后需要修改的样式. 未点击时: 点击之后: :active.:hover.:

C++虚基类详解

1.虚基类的作用从上面的介绍可知:如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员.在引用这些同名的成员时,必须在派生类对象名后增加直接基类名,以避免产生二义性,使其惟一地标识一个成员,如    c1.A::display( ).在一个类中保留间接共同基类的多份同名成员,这种现象是人们不希望出现的.C++提供虚基类(virtual base class )的方法,使得在继承间接共同基类时只保留一份成员.现在,将类A声明为

URLConnection类详解

为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/3753224.html URLConnection概述 URLConnection是一个抽象类,表示指向URL指定资源的活动连接. URLConnection类本身依赖于Socket类实现网络连接.一般认为,URLConnection类提供了比Socket类更易于使用.更高级的网络连接抽象.但实际上,大多数程序员都会忽略它