现代编程语言那些让人激动的特性
xsobi 2024-11-24 00:28 1 浏览
引言
有没有觉得,发展到现在,软件开发行业是越来越成熟了,无论是过程管理、架构方法、设计方法,还是语言、平台、框架、工具等,都发展到了一个前所未有的高度,相关思想和理念也日臻完善,我们真正进入了一个最好的时代。
单就编程语言来说,近些年包括Scala(2003)、Groovy(2003)、Go(2009)、Kotlin(2011)、Swift(2014)等新兴编程语言如雨后春笋版涌现出来,也给我们带来了很多让人眼前一亮的编程特性,甚至Java这等老牌编程语言也是不断推陈出新,编程再也不像过去那般枯燥。
本篇就带大家一起感受一下现代编程语言那些激动人心的特性。
原型(Prototype)
这个特性其实有点早了,但是也是很早就让人感动的语言特性了,熟悉Javascript的同学应该对它很了解。Javascript语言具有动态性,我们可以随时为类的某个实例添加方法,也可以利用动态原型,为类的所有实例添加方法,有没有感觉扩展类的实现变得非常方便了呢?
//声明一个Person类
class Person {
constructor(name, birthdate) {
this.name = name;
this.birthdate = birthdate;
}
}
//实例化两个Person对象
let person1 = new Person("张三", Date.now());
let person2 = new Person("李四", Date.now());
//为其中一个person对象动态添加名为debug的方法,并调用
person1.debug = function() {
console.debug(this);
}
person1.debug();
//利用动态原型为Person类添加名为debug的方法
Person.prototype.debug = function() {
console.debug(this);
}
//实例化一个新的Person对象,并调用其debug方法
let person3 = new Person("王五", Date.now());
person3.debug();
扩展(Extension)
扩展和原型很像,允许我们在不修改或继承类的情况下,将新的函数方法添加到原类中。这个特性较早见于C#这门语言,目前在Kotlin、Swift中均可以看到。这里顺便说一下C#,当时C#出来的时候,不得不说很多特性是非常棒的,包括扩展方法、泛型、分部类等等,比Java好不要太多。像Kotlin,不仅可以扩展类的方法,还可以扩展类的属性。
// Java中,通过继承String类来添加capitalize方法,实现首字母大写
public class StringCapitalize extends String {
public String capitalize() {
var character = this.substring(0, 1).toUpperCase();
var rest = this.substring(1, this.length() - 1).toLowerCase();
return character + rest;
}
}
// Java中,通过添加工具类扩展String类
public class StringUtils {
public static String capitalize(String string) {
var character = string.substring(0, 1).toUpperCase();
var rest = string.substring(1, string.length() - 1).toLowerCase();
return character + rest;
}
}
// Kotlin中,通过Extension特性,为String类添加方法。作用域内所有的String实例均有了这个方法。
fun String.capitalize(): String {
val character = substring(0, 1).toUpperCase()
val rest = substring(1, length - 1).toLowerCase()
return character + rest
}
隐式接口实现
前两个都是关于扩展代码的,这里再来一个。我们知道Java 1.8以来,接口interface里的方法可以有自己的默认实现了,大大方便了实现类,减少了重复代码。相对于Java的这个实现是显示的,Go语言的接口实现可以是隐式的,添加隐式实现后,所有继承的结构(Go没有类,都是结构struct)都可以调用这个方法,和前面的两个特性有异曲同工之妙,下面我们对比看一下。
// 这是Java的接口,里面默认显示实现了display方法
public interface Shape {
float area();
float perimeter();
default void display() {
System.out.println(area());
}
}
// 这个类实现了上面的接口,其实例对象可以直接调用display方法
public class Rectangle implements Shape {
public final float width;
public final float height;
public Rectangle(float width, float height) {
this.width = width;
this.height = height;
}
//.....中间省略若干代码...
public static void main(String... args) {
var rect = new Rectangle(2.0f, 3.0f);
rect.display(); //调用接口中的默认实现
}
}
// ------------------以下为 Go语言实现 -----------------
package main
import (
"fmt"
)
// 接口定义
type shape interface {
area() float32
perimeter() float32
}
// 结构定义
type rectangle struct {
width float32
height float32
}
// 添加area、perimeter函数到rectangle struct
func (rect rectangle) area() float32 {
return rect.width * rect.height
}
func (rect rectangle) perimeter() float32 {
return 2 * rect.width + 2 * rect.height
}
// 为shape接口隐式添加display方法及实现
func display(shape shape) {
fmt.Println(shape.area())
}
// 为实例调用display方法
func main() {
rect := rectangle{width: 2, height: 3}
display(rect)
}
宏(Macro)
C语言就有宏的概念,通过 #define 定义,然后在代码中进行替换。宏作为Rust语言的高级特性,可以操作语法单元,是一种通过编写代码来生成代码的方式,被称作“元编程”(meta programming)。相对于函数,宏可以接受任意多个参数,可以减少重复代码,定义DSL。宏语法比较复杂,难以编写和调试,以至于在Rust文档中说,宏将是其最后的特性。
// 定义一个生成函数的宏,将通过模式匹配生成函数
macro_rules! create_function {
($func_name:ident) => (
fn $func_name() {
println!("function {:?} is called", stringify!($func_name))
}
)
}
fn main() {
// 调用之前定义的宏
create_function!(foo); // 也可以这样:create_function![foo];
foo();
}
// 定义名为vector的宏,会根据第18行模式匹配的次数,重复第21行语句相同次数
macro_rules! vector {
($($x:expr),*) => {
{
let mut temp_vec = Vec::new();
$(temp_vec.push($x);)*
temp_vec
}
};
}
// 调用上面的宏生成的函数,创建一个Vector
fn main() {
let a = vector![1, 2, 4, 8];
println!("{:?}", a);
}
// 相当于执行了如下代码
{
let mut temp_vec = Vec::new();
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(4);
temp_vec.push(8);
temp_vec
}
自动属性
当你回想写代码枯燥的时候,应该会想到为字段编写getter、setter吧?较早的时候,C#就意识到了这个问题,贴心地推出了自动属性这个语法糖。而Java开发者则是通过Eclipse、IDEA这样的开发工具来自动生成getter、setter代码。当然,现在也可以依赖Lombook包,使用lombok的注解@Getter @Setter来编译时生成相关代码。
// Java:需要手动编写getter、setter
public class Person {
private String name;
public setName(String name) {
this.name = name;
}
public getName() {
return this.name;
}
}
// C#:有了自动属性,是不是省事多了?
public class Person
{
public string Name { get; private set; }
}
可选链(optional chaining)
据说空指针异常是软件业最贵的异常,价值10亿美元。你有没有为处理调用链中的null值而烦恼过?又或者被伤害过?Kotlin会在编译期提示对可能为null变量的不安全使用,也提供了Elvis 操作符 ?: 来方便地处理null值。而有了可选链,就舒服多了。可选链语法应该较早出现在JavaScript语言中,新兴语言Swift也提供了这一省心的特性。Swift英明地决定变量是不允许直接存储NIL值,当然也提供了optionals的装箱功能允许将NIL或其它值包装起来,方便有时使用。
// --------------------这个是JavaScript里的示例--------------------------
const obj = { a: { b: [{ name: 'obj' }] } }
// 原本的写法
console.log(obj && obj.a && obj.a.b.length && obj.a.b[0].name)
// 可选链写法,是不是很酷?
console.log(obj?.a?.b?.[0]?.name); // obj
console.log(obj?.b?.c?.d) // undefined
// --------------------这个是Swift里的示例--------------------------
class Person {
var residence: Residence? //这里是一个optional的语法,该字段可存储NIL值
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
// 链接可选residence?属性,如果residence存在则取回numberOfRooms的值
if let roomCount = john.residence?.numberOfRooms {
print("John 的房间号为 \(roomCount)。")
} else {
print("不能查看房间号")
}
卫语句(Guard)
输入乃万恶之源,函数首要的事情就是检查不规范和不安全的输入,这也是卫语句的来历。Swift语言为此提供了专门的卫语句语法,有了它的贴身防护,整个代码都干爽多了,剧烈运动都不怕,不信往下瞧:
// 异常枚举
enum CircleAreaCalculationError: ErrorType {
case RadiusNotSpecified
case RadiusUnvalid
}
// 传统的卫语句写法(当然可以写的更简洁,这个写法明显是故意的)
func calculateCirlceArea(radius:Double?) throws -> Double {
if let radius = radius {
if radius > 0 {
return radius*radius*M_PI
} else {
throw CircleAreaCalculationError.RadiusUnvalid
}
} else {
throw CircleAreaCalculationError.RadiusNotSpecified
}
}
// 使用guard关键字的写法,代码不再嵌套那么多了吧
func calculateCirleArea(radius:Double?) throws -> Double {
guard let radius = radius else {
throw CircleAreaCalculationError.RadiusNotSpecified
}
guard radius > 0 else {
throw CircleAreaCalculationError.RadiusUnvalid
}
return radius*radius*M_PI
}
Lambda表达式
如果要评选最酷的语言特性,那么Lambda表达式必须获得提名。Lambda表达式很早就出现在Lisp语言中,python也有,在后来的C#语言大放异彩,又一次狠狠地羞辱了不长进的Java,而Java也终于在1.8版本后加入了这一特性,甚至C++ 11也光荣地上车了。
// JDK7 匿名内部类写法
List<String> list = Arrays.asList("I", "love", "you", "too");
Collections.sort(list, new Comparator<String>(){// 接口名
@Override
public int compare(String s1, String s2){// 方法名
if(s1 == null)
return -1;
if(s2 == null)
return 1;
return s1.length()-s2.length();
}
});
// JDK8 Lambda表达式写法
List<String> list = Arrays.asList("I", "love", "you", "too");
Collections.sort(list, (s1, s2) ->{// <-----注意这里!!!!,是不是很酷?
if(s1 == null)
return -1;
if(s2 == null)
return 1;
return s1.length()-s2.length();
});
// JDK8 Lambda表达式写法
new Thread(
() -> System.out.println("Thread run()") // <-------还有这里!!!帅不帅?
).start();
// python 中的lambda
def make_incrementor(n):
return lambda x: x + n
addFive = make_incrementor(5)
addFive(10) # 返回15,动态生成函数,腻害不腻害?
渐进式类型(Gradual Typing)
我们知道编程语言有静态和动态之分,静态语言如Java 、 C# 、 C 和 C++,动态语言如Perl,Python,JavaScript,Ruby 和 PHP等,多数为脚本语言。而融合了静态和动态特性的语音,就被称为渐进式语言,如TypeScript、Common LISP、Dylan、Cecil、Visual Basic.NET、Bigloo Scheme、Strongtalk等。静态类型检查可以尽早地发现 BUG,动态类型检查可以方便地处理依赖于运行时信息的值的类型。渐进式语言允许类型注释来控制程序的一部分使用静态类型检查,而另一部分为动态检查,更具灵活性。Python从3.5开始引入了对静态类型检查的支持。
# 静态检查,已注明参数类型为float
def divide(dividend: float, divisor: float) -> float:
return dividend / divisor
# 动态检查,未注明参数类型
def divide(dividend, divisor):
return dividend / divisor
不变性(Immutability)
在面向对象的编程语言中,状态是计算的基础。由于可变状态的存在,在编写高并发,多线程代码时,无法知道并行进行的诸多状态读写中是否有顺序上的错误,而且这种错误又是难以察觉的,而不变性则规避了这个问题。不变性是函数式编程的基础,不变性意味着函数没有副作用,无论多少次执行,相同的输入就意味着相同的输出,所有线程都可以无所顾忌的执行同一个函数的代码,代码更像数学函数,更易理解和测试。
面向对象的编程通过封装可变动的部分来构造出可让人读懂的代码,函数式编程则是通过最小化可变动的部分来构造出可让人读懂的代码。
String就是构建在Java语言内核中的不可变类的一个典型例子。Java 的 CopyOnWrite系列容器类也是利用了不变性增强了并发安全性。Java可以通过final修饰符实现类和变量的不可变。而Scala、Swift、Groovy等语言也有各自的语法实现不可变的变量和类。
//Java、Groovy的不变量
private final String immutable;
//Scala的不变量,注意val,而不是var
val immutable = 1
// Swift使用let声明不变量,而不是var
let immutable = 1
// Groovy使用注解声明不可变类型
@Immutable
class Preson {
}
// Swift实现不可变类
struct Person {
let name:String
let interests:[String]
}
多重分派(Multiple Dispatch)
多重分派是一些编程语言的特性,其中的函数或者方法,可以在运行时间(动态的)使用一个或多个实际参数的组合特征,路由动态分派至实现函数或方法。多重分派主要区别于我们常见的重载方法,重载方法是在编译期就绑定了,而多重分派是在运行期分派的。Lisp、Julia、C#、Groovy等语言内建多分派特性,JavaScript、Python和C等语言通过扩展支持多分派。多重分派可以避免我们写很多分支条件,而是更直观地用对象类型表达,使代码变得可读性更好并且较少发生错误。
//Julia多分派示例
collide_with(x::Asteroid, y::Asteroid) = ... # deal with asteroid hitting asteroid
collide_with(x::Asteroid, y::Spaceship) = ... # deal with asteroid hitting spaceship
collide_with(x::Spaceship, y::Asteroid) = ... # deal with spaceship hitting asteroid
collide_with(x::Spaceship, y::Spaceship) = ... # deal with spaceship hitting spaceship
// 以下是c#多分派示例(注意关键字dynamic)
class Program
{
static void Main()
{
Console.WriteLine(Collider.Collide(new Asteroid(101), new Spaceship(300)));
Console.WriteLine(Collider.Collide(new Asteroid(10), new Spaceship(10)));
Console.WriteLine(Collider.Collide(new Spaceship(101), new Spaceship(10)));
}
}
static class Collider
{
public static string Collide(SpaceObject x, SpaceObject y) =>
((x.Size > 100) && (y.Size > 100)) ?
"Big boom!" : CollideWith(x as dynamic, y as dynamic);
private static string CollideWith(Asteroid x, Asteroid y) => "a/a";
private static string CollideWith(Asteroid x, Spaceship y) => "a/s";
private static string CollideWith(Spaceship x, Asteroid y) => "s/a";
private static string CollideWith(Spaceship x, Spaceship y) => "s/s";
}
abstract class SpaceObject
{
public SpaceObject(int size) => Size = size;
public int Size { get; }
}
class Asteroid : SpaceObject
{
public Asteroid(int size) : base(size) { }
}
class Spaceship : SpaceObject
{
public Spaceship(int size) : base(size) { }
}
// 以下是python用多分派实现的剪刀、石头、布
from multipledispatch import dispatch
@dispatch(Rock, Rock)
def beats3(x, y): return None
@dispatch(Rock, Paper)
def beats3(x, y): return y
@dispatch(Rock, Scissors)
def beats3(x, y): return x
@dispatch(Paper, Rock)
def beats3(x, y): return x
@dispatch(Paper, Paper)
def beats3(x, y): return None
@dispatch(Paper, Scissors)
def beats3(x, y): return x
@dispatch(Scissors, Rock)
def beats3(x, y): return y
@dispatch(Scissors, Paper)
def beats3(x, y): return x
@dispatch(Scissors, Scissors)
def beats3(x, y): return None
@dispatch(object, object)
def beats3(x, y):
if not isinstance(x, (Rock, Paper, Scissors)):
raise TypeError("Unknown first thing")
else:
raise TypeError("Unknown second thing")
解构(Destructuring)
前面几个特性是不是略显沉闷,那么来看一下这个激动一下。解构这一语法特性用于从数组索引或对象属性创建变量,简直帅到飞起。
// JavaScript ES6 的解构示例
var [ foo ] = array;
var foo = array[someIndex];
// 还是JavaScript的,有没有受惊???
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(rest);
// 将输出: Array [30,40,50]
// 以下3个是Python的解构
start, *_ = [1, 4, 3, 8]
print(start) # 输出 1
print(_) # 输出 [4, 3, 8]
*_, end = ["red", "blue", "green"]
print(end) # 输出 "green"
start, *_, (last_word_first_letter, *_) = ["Hi", "How", "are", "you?"]
print(last_word_first_letter) # 输出 "y"
内联测试(Inline Testing)
爱写单元测试的同学有福了,这个绝壁是重磅炸弹,在生产代码里夹着测试代码,你有想过这么写测试吗?谁想的?简直脑洞打开啊!该特性在Pyret语言中,Pyret旨在作为编程教育的杰出选择,同时探索脚本和函数式编程的融合。
// Pyret
fun sum(l):
cases (List) l:
| empty => 0
| link(first, rest) => first + sum(rest)
end
where: //这里就是测试代码啦,简直要疯了
sum([list: ]) is 0
sum([list: 1, 2, 3]) is 6
end
//复杂一点的
eps = 0.001
fun d-dx(f):
doc: "Approximate the derivative of f"
lam(x): (f(x + eps) - f(x)) / eps end
where:
fun square(x): x * x end
fun around(delta, target):
lam(actual): num-abs(actual - target) < delta end
end
dsquare = d-dx(square)
dsquare(5) satisfies around(0.1, 10)
dsquare(10) satisfies around(0.1, 20)
end
内联编译期(Inline Assemblers)
如果内联测试没有让你震惊,D语言内联编译期的这个特性绝对会让你惊掉下巴,基于该特性,开发人员可以直接在D语言中嵌入汇编代码,彻底放飞自我了,俺滴亲娘啊!受不了!受不了!顺便说一下,D语言比较小众,是C++的一个改进型,它包括了按合约设计、垃圾回收、关联数组、数组切片和惰性求值等特性。
void *pc;
asm
{
pop EBX ;
mov pc[EBP], EBX ;
}
模式匹配(pattern matching)
好吧,我们看点其它的来压压惊吧。尽管Kotlin语言也说自己实现了模式匹配,但是实际上只是一点点帅,真正帅的是Elixir语言的模式匹配,Elixir作为一种在Erlang OTP上运行的动态类型语言,将模式匹配提升到了一个全新的水平。
// Kotlin所谓的模式匹配,虽然很帅,但是还有switch的影子
var statusCode: Int
val errorMessage = when(statusCode) {
401 -> "Unauthorized"
403 -> "Forbidden"
500 -> "Internal Server Error"
else -> "Unrecognized Status Code"
}
val errorMessage = when {
statusCode == 401 -> "Unauthorized"
statusCode == 403 -> "Forbidden"
statusCode - 400 < 100 -> "Client Error"
statusCode == 500 -> "Internal Server Error"
statusCode - 500 < 100 -> "Server Error"
else -> "Unrecognized Status Code"
}
// Elixir 的模式匹配,实现变量的解构,a、b、c将分别依次被赋值
{a, b, c} = {:hello, "world", 42}
// head 将等于1, tail将等于[2, 3]
[head | tail] = [1, 2, 3]
(声明性循环)for-comprehensions
在编程语法上,Python真是个神一样的存在,for循环都能写出花来。
// Python第一种写法
numbers = []
for n in [1, 2, 3, 4, 5]:
numbers.append(n)
print(numbers)
//Python 的 for-comprehensions 写法
numbers = [n for n in [1, 2, 3, 4, 5]]
print(numbers) # 输出[1, 2, 3, 4, 5]
// 还可以计算
numbers = [n ** 2 for n in [1, 2, 3, 4, 5]]
print(numbers) #输出 [1, 4, 9, 16, 25]
// 还能过滤
numbers = [n ** 2 for n in [1, 2, 3, 4, 5] if n % 2 == 0]
print(numbers) # 输出[4, 16]
// 还能组合
numbers = [a:n for n in [1, 2, 3] for a in ['a', 'b']]
print(numbers) # 输出[('a', 1), ('b', 1), ('a', 2), ('b', 2), ('a', 3), ('b', 3)]
// 这不,把Scala也带坏了
val executionTimeList = List(("test 1", 100), ("test 2", 230))
val numberOfAssertsWithExecutionTime: List[(String, Int, Int)] =
for {
result <- results
(id, time) <- executionTimeList
if result.id == id
} yield ((id, result.totalAsserts, time))
// numberOfAssertsWithExecutionTime 为 List[("test 1", 10, 100), ("test 2", 6, 230)]
流(Stream)
Java 8 中提供了Stream API特性, Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式,来提供一种对 Java 集合运算和表达的高阶抽象。事实上这个特性C#早就有了(Java又躺枪一次)。不得不说,利用这个特性写出来的代码,看上去还真的是很流利的。
// Java 的 Stream API 语法
List<Integer> transactionsIds =
widgets.stream()
.filter(b -> b.getColor() == RED)
.sorted((x,y) -> x.getWeight() - y.getWeight())
.mapToInt(Widget::getWeight)
.sum();
// c#的LINQ示例
int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
var numQuery =
from num in numbers
where (num % 2) == 0
select num;
分部(Partial)
分部类、分部方法是C#提供的一个很棒的特性,它允许开发人员将类的代码显示地组织到不同的文件中,以方便管理,但不影响编译和使用。如将UI编辑工具自动生成的代码和人工编写的代码分到不同的代码文件,也可以将方法的声明和实现分开,也可以根据其它代码组织需要进行划分,还是很贴心的。
//--------------------------------一以下为分部类的示例----------------------
public partial class Employee
{
public void DoWork()
{
}
}
public partial class Employee
{
public void GoToLunch()
{
}
}
//--------------------------------一以下为分部方法的示例----------------------
// 方法的定义在 file1.cs 中
partial void OnNameChanged();
// 方法的实现在 file2.cs 中
partial void OnNameChanged()
{
// 方法体
}
结语
好了,到这里就基本介绍结束了,应该还有很多有意思的特性,如泛型(generic)、高阶函数(higher order functions)、惰性计算(laziness)等,很多见于函数式编程语言,就不一一介绍了。而我们也会注意到,编程语言之间也在不断地相互学习和借鉴,很多语言的特性也都有其两面性,给我们带来方便的同时,有时也会滋生困扰,引起可读性等问题,在使用上要注意扬长避短。
- 上一篇:SQL Server 可观测最佳实践
- 下一篇:arthas诊断工具入门
相关推荐
- 好用的云函数!后端低代码接口开发,零基础编写API接口
-
前言在开发项目过程中,经常需要用到API接口,实现对数据库的CURD等操作。不管你是专业的PHP开发工程师,还是客户端开发工程师,或者是不懂编程但懂得数据库SQL查询,又或者是完全不太懂技术的人,通过...
- 快速上手:Windows 平台上 cURL 命令的使用方法
-
在工作流程中,为了快速验证API接口有效性,团队成员经常转向直接执行cURL命令的方法。这种做法不仅节省时间,而且促进了团队效率的提升。对于使用Windows系统的用户来说,这里有一套详细...
- 使用 Golang net/http 包:基础入门与实战
-
简介Go的net/http包是构建HTTP服务的核心库,功能强大且易于使用。它提供了基本的HTTP客户端和服务端支持,可以快速构建RESTAPI、Web应用等服务。本文将介绍ne...
- #小白接口# 使用云函数,人人都能编写和发布自己的API接口
-
你只需编写简单的云函数,就可以实现自己的业务逻辑,发布后就可以生成自己的接口给客户端调用。果创云支持对云函数进行在线接口编程,进入开放平台我的接口-在线接口编程,设计一个新接口,设计和配置好接口参...
- 极度精神分裂:我家没有墙面开关,但我虚拟出来了一系列开关
-
本内容来源于@什么值得买APP,观点仅代表作者本人|作者:iN在之前和大家说过,在iN的家里是没有墙面开关的。...
- window使用curl命令的注意事项 curl命令用法
-
cmd-使用curl命令的注意点前言最近在cmd中使用curl命令来测试restapi,发现有不少问题,这里记录一下。在cmd中使用curl命令的注意事项json不能由单引号包括起来json...
- Linux 系统curl命令使用详解 linuxctrl
-
curl是一个强大的命令行工具,用于在Linux系统中进行数据传输。它支持多种协议,包括HTTP、HTTPS、FTP等,用于下载或上传数据,执行Web请求等。curl命令的常见用法和解...
- Tornado 入门:初学者指南 tornados
-
Tornado是一个功能强大的PythonWeb框架和异步网络库。它最初是为了处理实时Web服务中的数千个同时连接而开发的。它独特的Web服务器和框架功能组合使其成为开发高性能Web...
- PHP Curl的简单使用 php curl formdata
-
本文写给刚入PHP坑不久的新手们,作为工具文档,方便用时查阅。CURL是一个非常强大的开源库,它支持很多种协议,例如,HTTP、HTTPS、FTP、TELENT等。日常开发中,我们经常会需要用到cur...
- Rust 服务器、服务和应用程序:7 Rust 中的服务器端 Web 应用简介
-
本章涵盖使用Actix提供静态网页...
- 我给 Apache 顶级项目提了个 Bug apache顶级项目有哪些
-
这篇文章记录了给Apache顶级项目-分库分表中间件ShardingSphere提交Bug的历程。说实话,这是一次比较曲折的Bug跟踪之旅。10月28日,我们在GitHub上提...
- linux文件下载、服务器交互(curl)
-
基础环境curl命令描述...
- curl简单使用 curl sh
-
1.curl--help#查看关键字2.curl-A“(添加user-agent<name>SendUser-Agent<name>toserver)”...
- 常用linux命令:curl 常用linux命令大全
-
//获取网页内容//不加任何选项使用curl时,默认会发送GET请求来获取内容到标准输出$curlhttp://www.baidu.com//输出<!DOCTYPEh...
- 三十七,Web渗透提高班之hack the box在线靶场注册及入门知识
-
一.注册hacktheboxHackTheBox是一个在线平台,允许测试您的渗透技能和代码,并与其他类似兴趣的成员交流想法和方法。它包含一些不断更新的挑战,并且模拟真实场景,其风格更倾向于CT...
- 一周热门
- 最近发表
-
- 好用的云函数!后端低代码接口开发,零基础编写API接口
- 快速上手:Windows 平台上 cURL 命令的使用方法
- 使用 Golang net/http 包:基础入门与实战
- #小白接口# 使用云函数,人人都能编写和发布自己的API接口
- 极度精神分裂:我家没有墙面开关,但我虚拟出来了一系列开关
- window使用curl命令的注意事项 curl命令用法
- Linux 系统curl命令使用详解 linuxctrl
- Tornado 入门:初学者指南 tornados
- PHP Curl的简单使用 php curl formdata
- Rust 服务器、服务和应用程序:7 Rust 中的服务器端 Web 应用简介
- 标签列表
-
- grid 设置 (58)
- 移位运算 (48)
- not specified (45)
- patch补丁 (31)
- strcat (25)
- 导航栏 (58)
- context xml (46)
- scroll (43)
- element style (30)
- dedecms模版 (53)
- vs打不开 (29)
- nmap (30)
- webgl开发 (24)
- parse (24)
- c 视频教程下载 (33)
- paddleocr (28)
- listview排序 (33)
- firebug 使用 (31)
- transactionmanager (30)
- characterencodingfilter (33)
- getmonth (34)
- commandtimeout (30)
- hibernate教程 (31)
- label换行 (33)
- curlpost (31)